<commit_before>#include <atomic>
#include <chrono>
#include <ctime>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <string>
#include <tuple>

#include <cassert>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>

#include "logging.hpp"

namespace io
{
    class selectable: public std::enable_shared_from_this<selectable>
    {
        public:
            virtual int fd() = 0;
            virtual void do_read() = 0;
            virtual void do_write() = 0;
            virtual ~selectable() {};

            void do_close();
    };

    typedef std::tuple<std::chrono::milliseconds, std::function<void()>> delayed_call;

    class loop
    {
        std::map<int, std::shared_ptr<io::selectable>> selectables;

        std::deque<delayed_call> delayed_calls;

        int epoll_fd;
        std::atomic<bool> running;

        public:
        loop() : epoll_fd(-1), running(false) {};
        ~loop();

        void init();
        void add_selectable(std::shared_ptr<io::selectable> selectable);
        void remove_selectable(int fd);
        void call_soon(std::function<void()> func);
        void call_later(std::chrono::milliseconds delay, std::function<void()> func);
        void stop();

        static loop* instance() {
            static loop ev;
            return &ev;
        };

        void run();
        void run_delayed_calls(const std::chrono::milliseconds &elapsed);
    };

    std::string error(int errnum)
    {
        std::string msg;
        char buf[512];
        msg = strerror_r(errnum, buf, 512);
        return msg + " (" + std::to_string(errnum) + ")";
    }

    std::tuple<int, std::string> write(int fd, std::string buf)
    {
        int written = ::write(fd, buf.c_str(), buf.size());

        if (written < 0) {
            int xerrno = errno;
            logger(2, "write error " << io::error(xerrno));
            close(fd);
        } else {
            buf = buf.substr(written, std::string::npos);
        }

        return std::make_tuple(written, buf);
    }

    std::string consume(int fd)
    {
        std::string msg;
        ssize_t rsize;
        char buf[512];

        while (true) {
            memset(&buf, 0, 512);
            rsize = read(fd, buf, sizeof(buf));
            logger(5, "read " << rsize << " bytes from " << fd);
            if (rsize == -1) {
                if (errno != EAGAIN) {
                    perror ("read");
                    // FIXME: raise exception. Would be cool to make sure if
                    // msg.size() that we... handle that in some way.
                }
                break;
            } else if (rsize == 0) {
                break;
            } else {
                msg += std::string(buf, rsize);
            }
        }
        return msg;
    }

    void nonblocking(int fd)
    {
        int flags;

        flags = fcntl (fd, F_GETFL, 0);
        if (flags == -1) {
            int xerrno = errno;
            logger(1, "failed to get fcntl flags for FD " << fd << io::error(xerrno));
            return;
        }

        flags |= O_NONBLOCK;
        if (-1 == fcntl(fd, F_SETFL, flags)) {
            int xerrno = errno;
            logger(1, "failed to set nonblocking for FD " << fd << io::error(xerrno));
            return;
        }
    };
}

void io::selectable::do_close()
{
    logger(3, "closing FD " << fd());
    io::loop::instance()->remove_selectable(fd());
    close(fd());
}

io::loop::~loop()
{
    logger(4, "loop destructed with " << delayed_calls.size() << " delayed calls remaining");
}

void io::loop::init()
{
    if ((this->epoll_fd = epoll_create1(0)) < 0) {
        int xerrno = errno;
        logger(1, "epoll_create error " << io::error(xerrno));
        return;
    }

    logger(4, "epoll fd created " << this->epoll_fd);

    running = true;
}

void io::loop::add_selectable(std::shared_ptr<io::selectable> selectable)
{
    assert(selectable);

    const int fd = selectable->fd();

    epoll_event ev;

    ev.data.fd = fd;
    ev.events = EPOLLIN | EPOLLET | EPOLLOUT;

    assert(this->epoll_fd != -1);

    selectables[fd] = selectable;

    if (epoll_ctl(this->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
        int xerrno = errno;
        logger(1, "epoll_ctl EPOLL_CTL_ADD failed for FD " << fd << io::error(xerrno));
    } else {
        logger(4, "selectable " << selectable << " added for FD " << fd);
    }
}

void io::loop::remove_selectable(int fd)
{
    assert(this->epoll_fd != -1);

    if (epoll_ctl(this->epoll_fd, EPOLL_CTL_DEL, fd, NULL) < 0) {
        int xerrno = errno;
        logger(1, "epoll_ctl EPOLL_CTL_DEL failed for fd " << fd << io::error(xerrno));
    } else {
        logger(4, "selectable FD " << fd << " removed");
    }

    selectables[fd] = nullptr;
}

void io::loop::call_soon(std::function<void()> func)
{
    delayed_calls.push_back(io::delayed_call(std::chrono::seconds(0), func));
};

void io::loop::call_later(std::chrono::milliseconds dur, std::function<void()> func)
{
    delayed_calls.push_back(io::delayed_call(dur, func));
};

void io::loop::stop()
{
    running = false;
}

void io::loop::run()
{
    epoll_event *events;

    const int MAXEVENTS = 64;

    events = (epoll_event*)calloc(MAXEVENTS, sizeof(epoll_event));

    std::shared_ptr<io::selectable> selectable;
    while (running) {
        int n;

        const auto start = std::chrono::steady_clock::now();

        n = epoll_wait(this->epoll_fd, events, MAXEVENTS, 100);
        if (n == -1) {
            int xerrno = errno;
            logger(1, "error calling epoll_wait" << io::error(xerrno));
        } else {
            logger(9, n << " events to process");
        }

        epoll_event *ev;
        for (int i=0; i < n; i++) {
            ev = &events[i];

            try {
                selectable = selectables[ev->data.fd];

                assert(selectable);

                if (ev->events & EPOLLERR || ev->events & EPOLLHUP || (!ev->events & (EPOLLIN|EPOLLOUT))) {
                    selectable->do_close();
                } else if (ev->events & EPOLLIN) {
                    selectable->do_read();
                } else if (ev->events & EPOLLOUT) {
                    selectable->do_write();
                }
            }
            catch (std::exception const & e) {
                logger(1, "error on selectable FD " << ev->data.fd << " " << e.what());
            }
        }
        const std::chrono::milliseconds elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);

        this->run_delayed_calls(elapsed);
    }
}

void io::loop::run_delayed_calls(const std::chrono::milliseconds &elapsed)
{
    delayed_calls.erase(
        std::remove_if(
            delayed_calls.begin(),
            delayed_calls.end(),
            [elapsed](io::delayed_call &delayed_call) -> bool {
                std::chrono::milliseconds delay;
                std::function<void()> call;
                std::tie(delay, call) = delayed_call;

                delay = delay - elapsed;
                if (delay.count() <= 0) {
                    call();
                    return true;
                } else {
                    std::get<0>(delayed_call) = delay;
                    return false;
                }
            }),
        delayed_calls.end()
    );
}
<commit_msg>Allowing child classes to override this seems like a good idea.<commit_after>#include <atomic>
#include <chrono>
#include <ctime>
#include <deque>
#include <functional>
#include <iomanip>
#include <iostream>
#include <string>
#include <tuple>

#include <cassert>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>

#include "logging.hpp"

namespace io
{
    class selectable: public std::enable_shared_from_this<selectable>
    {
        public:
            virtual int fd() = 0;
            virtual void do_read() = 0;
            virtual void do_write() = 0;
            virtual ~selectable() {};

            virtual void do_close();
    };

    typedef std::tuple<std::chrono::milliseconds, std::function<void()>> delayed_call;

    class loop
    {
        std::map<int, std::shared_ptr<io::selectable>> selectables;

        std::deque<delayed_call> delayed_calls;

        int epoll_fd;
        std::atomic<bool> running;

        public:
        loop() : epoll_fd(-1), running(false) {};
        ~loop();

        void init();
        void add_selectable(std::shared_ptr<io::selectable> selectable);
        void remove_selectable(int fd);
        void call_soon(std::function<void()> func);
        void call_later(std::chrono::milliseconds delay, std::function<void()> func);
        void stop();

        static loop* instance() {
            static loop ev;
            return &ev;
        };

        void run();
        void run_delayed_calls(const std::chrono::milliseconds &elapsed);
    };

    std::string error(int errnum)
    {
        std::string msg;
        char buf[512];
        msg = strerror_r(errnum, buf, 512);
        return msg + " (" + std::to_string(errnum) + ")";
    }

    std::tuple<int, std::string> write(int fd, std::string buf)
    {
        int written = ::write(fd, buf.c_str(), buf.size());

        if (written < 0) {
            int xerrno = errno;
            logger(2, "write error " << io::error(xerrno));
            close(fd);
        } else {
            buf = buf.substr(written, std::string::npos);
        }

        return std::make_tuple(written, buf);
    }

    std::string consume(int fd)
    {
        std::string msg;
        ssize_t rsize;
        char buf[512];

        while (true) {
            memset(&buf, 0, 512);
            rsize = read(fd, buf, sizeof(buf));
            logger(5, "read " << rsize << " bytes from " << fd);
            if (rsize == -1) {
                if (errno != EAGAIN) {
                    perror ("read");
                    // FIXME: raise exception. Would be cool to make sure if
                    // msg.size() that we... handle that in some way.
                }
                break;
            } else if (rsize == 0) {
                break;
            } else {
                msg += std::string(buf, rsize);
            }
        }
        return msg;
    }

    void nonblocking(int fd)
    {
        int flags;

        flags = fcntl (fd, F_GETFL, 0);
        if (flags == -1) {
            int xerrno = errno;
            logger(1, "failed to get fcntl flags for FD " << fd << io::error(xerrno));
            return;
        }

        flags |= O_NONBLOCK;
        if (-1 == fcntl(fd, F_SETFL, flags)) {
            int xerrno = errno;
            logger(1, "failed to set nonblocking for FD " << fd << io::error(xerrno));
            return;
        }
    };
}

void io::selectable::do_close()
{
    logger(3, "closing FD " << fd());
    io::loop::instance()->remove_selectable(fd());
    close(fd());
}

io::loop::~loop()
{
    logger(4, "loop destructed with " << delayed_calls.size() << " delayed calls remaining");
}

void io::loop::init()
{
    if ((this->epoll_fd = epoll_create1(0)) < 0) {
        int xerrno = errno;
        logger(1, "epoll_create error " << io::error(xerrno));
        return;
    }

    logger(4, "epoll fd created " << this->epoll_fd);

    running = true;
}

void io::loop::add_selectable(std::shared_ptr<io::selectable> selectable)
{
    assert(selectable);

    const int fd = selectable->fd();

    epoll_event ev;

    ev.data.fd = fd;
    ev.events = EPOLLIN | EPOLLET | EPOLLOUT;

    assert(this->epoll_fd != -1);

    selectables[fd] = selectable;

    if (epoll_ctl(this->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
        int xerrno = errno;
        logger(1, "epoll_ctl EPOLL_CTL_ADD failed for FD " << fd << io::error(xerrno));
    } else {
        logger(4, "selectable " << selectable << " added for FD " << fd);
    }
}

void io::loop::remove_selectable(int fd)
{
    assert(this->epoll_fd != -1);

    if (epoll_ctl(this->epoll_fd, EPOLL_CTL_DEL, fd, NULL) < 0) {
        int xerrno = errno;
        logger(1, "epoll_ctl EPOLL_CTL_DEL failed for fd " << fd << io::error(xerrno));
    } else {
        logger(4, "selectable FD " << fd << " removed");
    }

    selectables[fd] = nullptr;
}

void io::loop::call_soon(std::function<void()> func)
{
    delayed_calls.push_back(io::delayed_call(std::chrono::seconds(0), func));
};

void io::loop::call_later(std::chrono::milliseconds dur, std::function<void()> func)
{
    delayed_calls.push_back(io::delayed_call(dur, func));
};

void io::loop::stop()
{
    running = false;
}

void io::loop::run()
{
    epoll_event *events;

    const int MAXEVENTS = 64;

    events = (epoll_event*)calloc(MAXEVENTS, sizeof(epoll_event));

    std::shared_ptr<io::selectable> selectable;
    while (running) {
        int n;

        const auto start = std::chrono::steady_clock::now();

        n = epoll_wait(this->epoll_fd, events, MAXEVENTS, 100);
        if (n == -1) {
            int xerrno = errno;
            logger(1, "error calling epoll_wait" << io::error(xerrno));
        } else {
            logger(9, n << " events to process");
        }

        epoll_event *ev;
        for (int i=0; i < n; i++) {
            ev = &events[i];

            try {
                selectable = selectables[ev->data.fd];

                assert(selectable);

                if (ev->events & EPOLLERR || ev->events & EPOLLHUP || (!ev->events & (EPOLLIN|EPOLLOUT))) {
                    selectable->do_close();
                } else if (ev->events & EPOLLIN) {
                    selectable->do_read();
                } else if (ev->events & EPOLLOUT) {
                    selectable->do_write();
                }
            }
            catch (std::exception const & e) {
                logger(1, "error on selectable FD " << ev->data.fd << " " << e.what());
            }
        }
        const std::chrono::milliseconds elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start);

        this->run_delayed_calls(elapsed);
    }
}

void io::loop::run_delayed_calls(const std::chrono::milliseconds &elapsed)
{
    delayed_calls.erase(
        std::remove_if(
            delayed_calls.begin(),
            delayed_calls.end(),
            [elapsed](io::delayed_call &delayed_call) -> bool {
                std::chrono::milliseconds delay;
                std::function<void()> call;
                std::tie(delay, call) = delayed_call;

                delay = delay - elapsed;
                if (delay.count() <= 0) {
                    call();
                    return true;
                } else {
                    std::get<0>(delayed_call) = delay;
                    return false;
                }
            }),
        delayed_calls.end()
    );
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <string>

#include "plugin.hxx"
#include "compat.hxx"
#include "clang/AST/CXXInheritance.h"

// Final goal: Checker for VCL widget references. Makes sure that VCL Window subclasses are properly referenced counted and dispose()'ed.
//
// But at the moment it just finds subclasses of Window which are not heap-allocated
//
// TODO do I need to check for local and static variables, too ?
// TODO when we have a dispose() method, verify that the dispose() methods releases all of the Window references
// TODO when we have a dispose() method, verify that it calls the super-class dispose() method at some point.

namespace {

class VCLWidgets:
    public RecursiveASTVisitor<VCLWidgets>, public loplugin::Plugin
{
public:
    explicit VCLWidgets(InstantiationData const & data): Plugin(data) {}

    virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }

    bool VisitFieldDecl(const FieldDecl * decl);

    bool VisitParmVarDecl(ParmVarDecl const * decl);

    bool VisitVarDecl( const VarDecl* var );

};

bool BaseCheckNotWindowSubclass(const CXXRecordDecl *BaseDefinition, void *) {
    if (BaseDefinition->getQualifiedNameAsString().compare("vcl::Window") == 0) {
        return false;
    }
    return true;
}

bool isDerivedFromWindow(const CXXRecordDecl *decl) {
    if (!decl->hasDefinition())
        return false;
    if (decl->getQualifiedNameAsString().compare("vcl::Window") == 0)
        return true;
    if (// not sure what hasAnyDependentBases() does,
        // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
        !decl->hasAnyDependentBases() &&
        !decl->forallBases(BaseCheckNotWindowSubclass, nullptr, true)) {
        return true;
    }
    return false;
}

bool VCLWidgets::VisitFieldDecl(const FieldDecl * fieldDecl) {
    if (ignoreLocation(fieldDecl)) {
        return true;
    }
    if (fieldDecl->isBitField()) {
        return true;
    }
    const RecordType *recordType = fieldDecl->getType()->getAs<RecordType>();
    if (recordType == nullptr) {
        return true;
    }
    const CXXRecordDecl *recordDecl = dyn_cast<CXXRecordDecl>(recordType->getDecl());
    if (recordDecl == nullptr) {
        return true;
    }
    // check if this field is derived from Window
    if (!isDerivedFromWindow(recordDecl)) {
        return true;
    }

    report(
        DiagnosticsEngine::Warning,
        "vcl::Window subclass allocated as a class member, should be allocated via VclPtr.",
        fieldDecl->getLocation())
      << fieldDecl->getSourceRange();
    return true;
}

bool VCLWidgets::VisitParmVarDecl(ParmVarDecl const * pvDecl) {
    if (ignoreLocation(pvDecl)) {
        return true;
    }
    if (!pvDecl->getType()->isPointerType())
        return true;
    QualType pointeeType = pvDecl->getType()->getPointeeType();
    const RecordType *recordType = pointeeType->getAs<RecordType>();
    if (recordType == nullptr)
        return true;
    const CXXRecordDecl *recordDecl = dyn_cast<CXXRecordDecl>(recordType->getDecl());
    if (recordDecl == nullptr)
        return true;

    // check if this parameter is derived from Window
    if (isDerivedFromWindow(recordDecl)) {
        report(
            DiagnosticsEngine::Remark,
            "vcl::Window subclass passed as a pointer parameter, should be wrapped in VclPtr.",
            pvDecl->getLocation())
          << pvDecl->getSourceRange();
    }
    return true;
}

bool VCLWidgets::VisitVarDecl( const VarDecl* varDecl )
{
    if (ignoreLocation(varDecl)) {
        return true;
    }
    if (!varDecl->isLocalVarDecl())
        return true;
    if (!varDecl->getType()->isPointerType())
        return true;
    QualType pointeeType = varDecl->getType()->getPointeeType();
    const RecordType *recordType = pointeeType->getAs<RecordType>();
    if (recordType == nullptr)
        return true;
    const CXXRecordDecl *recordDecl = dyn_cast<CXXRecordDecl>(recordType->getDecl());
    if (recordDecl == nullptr)
        return true;

    // check if this variables type is derived from Window
    if (isDerivedFromWindow(recordDecl)) {
        report(
            DiagnosticsEngine::Remark,
            "vcl::Window subclass declared as a pointer var, should be wrapped in VclPtr.",
            varDecl->getLocation())
          << varDecl->getSourceRange();
    }

    return true;
}

loplugin::Plugin::Registration< VCLWidgets > X("vclwidgets");

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>clang plugin: check return types for vcl::Window* that should be wrapped<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <string>

#include "plugin.hxx"
#include "compat.hxx"
#include "clang/AST/CXXInheritance.h"

// Final goal: Checker for VCL widget references. Makes sure that VCL Window subclasses are properly referenced counted and dispose()'ed.
//
// But at the moment it just finds subclasses of Window which are not heap-allocated
//
// TODO do I need to check for local and static variables, too ?
// TODO when we have a dispose() method, verify that the dispose() methods releases all of the Window references
// TODO when we have a dispose() method, verify that it calls the super-class dispose() method at some point.

namespace {

class VCLWidgets:
    public RecursiveASTVisitor<VCLWidgets>, public loplugin::Plugin
{
public:
    explicit VCLWidgets(InstantiationData const & data): Plugin(data) {}

    virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }

    bool VisitFieldDecl(const FieldDecl * decl);

    bool VisitParmVarDecl(ParmVarDecl const * decl);

    bool VisitVarDecl( const VarDecl* var );

    bool VisitFunctionDecl( const FunctionDecl* var );
};

bool BaseCheckNotWindowSubclass(const CXXRecordDecl *BaseDefinition, void *) {
    if (BaseDefinition->getQualifiedNameAsString().compare("vcl::Window") == 0) {
        return false;
    }
    return true;
}

bool isDerivedFromWindow(const CXXRecordDecl *decl) {
    if (!decl->hasDefinition())
        return false;
    if (decl->getQualifiedNameAsString().compare("vcl::Window") == 0)
        return true;
    if (// not sure what hasAnyDependentBases() does,
        // but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
        !decl->hasAnyDependentBases() &&
        !decl->forallBases(BaseCheckNotWindowSubclass, nullptr, true)) {
        return true;
    }
    return false;
}

bool isPointerToWindowSubclass(const QualType& pType) {
    if (!pType->isPointerType())
        return false;
    QualType pointeeType = pType->getPointeeType();
    const RecordType *recordType = pointeeType->getAs<RecordType>();
    if (recordType == nullptr) {
        return false;
    }
    const CXXRecordDecl *recordDecl = dyn_cast<CXXRecordDecl>(recordType->getDecl());
    if (recordDecl == nullptr) {
        return false;
    }
    return isDerivedFromWindow(recordDecl);
}

bool VCLWidgets::VisitFieldDecl(const FieldDecl * fieldDecl) {
    if (ignoreLocation(fieldDecl)) {
        return true;
    }
    if (fieldDecl->isBitField()) {
        return true;
    }
    if (isPointerToWindowSubclass(fieldDecl->getType())) {
        report(
            DiagnosticsEngine::Remark,
            "vcl::Window subclass declared as a pointer field, should be wrapped in VclPtr.",
            fieldDecl->getLocation())
          << fieldDecl->getSourceRange();
        return true;
    }

    const RecordType *recordType = fieldDecl->getType()->getAs<RecordType>();
    if (recordType == nullptr) {
        return true;
    }
    const CXXRecordDecl *recordDecl = dyn_cast<CXXRecordDecl>(recordType->getDecl());
    if (recordDecl == nullptr) {
        return true;
    }
    // check if this field is derived from Window
    if (!isDerivedFromWindow(recordDecl)) {
        return true;
    }

    report(
        DiagnosticsEngine::Warning,
        "vcl::Window subclass allocated as a class member, should be allocated via VclPtr.",
        fieldDecl->getLocation())
      << fieldDecl->getSourceRange();
    return true;
}

bool VCLWidgets::VisitParmVarDecl(ParmVarDecl const * pvDecl) {
    if (ignoreLocation(pvDecl)) {
        return true;
    }
    // check if this parameter is derived from Window
    if (isPointerToWindowSubclass(pvDecl->getType())) {
        report(
            DiagnosticsEngine::Remark,
            "vcl::Window subclass passed as a pointer parameter, should be wrapped in VclPtr.",
            pvDecl->getLocation())
          << pvDecl->getSourceRange();
    }
    return true;
}

bool VCLWidgets::VisitVarDecl( const VarDecl* varDecl )
{
    if (ignoreLocation(varDecl)) {
        return true;
    }
    if (!varDecl->isLocalVarDecl())
        return true;

    // check if this variables type is derived from Window
    if (isPointerToWindowSubclass(varDecl->getType())) {
        report(
            DiagnosticsEngine::Remark,
            "vcl::Window subclass declared as a pointer var, should be wrapped in VclPtr.",
            varDecl->getLocation())
          << varDecl->getSourceRange();
    }

    return true;
}

bool VCLWidgets::VisitFunctionDecl( const FunctionDecl* functionDecl )
{
    if (ignoreLocation(functionDecl)) {
        return true;
    }
    QualType t1 { compat::getReturnType(*functionDecl) };
    // check if this variables type is derived from Window
    if (isPointerToWindowSubclass(t1)) {
        report(
            DiagnosticsEngine::Remark,
            "vcl::Window subclass declared as a return type from a method/function, should be wrapped in VclPtr.",
            functionDecl->getLocation())
          << functionDecl->getSourceRange();
    }
    return true;
}

loplugin::Plugin::Registration< VCLWidgets > X("vclwidgets");

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#ifndef DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_MATRIX_HH
#define DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_MATRIX_HH

// std includes
#include <vector>

// local includes
//#include "vector.hh"

namespace Dune {

namespace Detailed {

namespace Discretizations {

namespace Assembler {

namespace Local {

namespace Codim1 {

/**
  \todo Add neumann boundary treatment
  \todo When adding neumann: think of numTmpObjectsRequired()!
  \todo Add vector assembler
  \todo Add penalty parameter
  **/
template <class LocalInnerOperatorImp /*, class LocalDirichletOperatorImp*/ /*, class LocalNeumannOperatorImp*/>
class Matrix
{
public:
  typedef LocalInnerOperatorImp LocalInnerOperatorType;

  //  typedef LocalDirichletOperatorImp
  //    LocalDirichletOperatorType;

  //  typedef LocalNeumannOperatorImp
  //    LocalNeumannOperatorType;

  typedef Matrix<LocalInnerOperatorType /*, LocalDirichletOperatorType*/ /*, LocalNeumannOperatorType*/> ThisType;

  typedef typename LocalInnerOperatorType::RangeFieldType RangeFieldType;

  //  template< class InducingDiscreteFunctionType >
  //  class LocalVectorAssembler
  //  {
  //  private:
  //    typedef typename LocalOperatorType::template LocalFunctional< InducingDiscreteFunctionType >::Type
  //      InducingFunctionalType;

  //  public:
  //    typedef Dune::Functionals::Assembler::Local::Codim0::Vector< InducingFunctionalType >
  //      Type;
  //  };

  //! constructor
  Matrix(const LocalInnerOperatorType& localInnerOperator /*,
          const LocalDirichletOperatorType localDirichletOperator*/ /*,
          const LocalNeumannOperatorType localNeumannOperator*/)
    : localInnerOperator_(localInnerOperator) /*,
    localDirichletOperator_( localDirichletOperator )*/ /*,
localNeumannOperator_( localNeumannOperator )*/
  {
  }

private:
  //! copy constructor
  Matrix(const ThisType& other);

public:
  const LocalInnerOperatorType& localInnerOperator() const
  {
    return localInnerOperator_;
  }

  //  const LocalDirichletOperatorType& localDirichletOperator() const
  //  {
  //    return localDirichletOperator_;
  //  }

  //  const LocalNeumannOperatorType& localNeumannOperator() const
  //  {
  //    return localNeumannOperator_;
  //  }

  //  template< class InducingDiscreteFunctionType >
  //  typename LocalVectorAssembler< InducingDiscreteFunctionType >::Type
  //    localVectorAssembler( const InducingDiscreteFunctionType& inducingDiscreteFunction ) const
  //  {
  //    typedef typename LocalVectorAssembler< InducingDiscreteFunctionType >::Type
  //      LocalVectorAssemblerType;

  //    return LocalVectorAssemblerType( localOperator_.localFunctional( inducingDiscreteFunction ) );
  //  }

  /**
    \todo Add neumann treatment here!
    **/
  std::vector<unsigned int> numTmpObjectsRequired() const
  {
    std::vector<unsigned int> ret(2, 0);
    // we require 4 tmp matrix in this local assembler
    ret[0] = 4;
    // the operator itself requires that much local matrices
    ret[1] = /*std::max(*/ localInnerOperator_
                 .numTmpObjectsRequired() /*, localDirichletOperator_.numTmpObjectsRequired() )*/;
    return ret;
  }

  template <class AnsatzSpaceType, class TestSpaceType, class EntityType, class SystemMatrixType, class LocalMatrixType>
  void assembleLocal(const AnsatzSpaceType& ansatzSpace, const TestSpaceType& testSpace, const EntityType& entity,
                     SystemMatrixType& systemMatrix,
                     std::vector<std::vector<LocalMatrixType>>& tmpLocalMatricesContainer) const
  {
    // get the local basefunction sets
    typedef typename AnsatzSpaceType::BaseFunctionSetType::LocalBaseFunctionSetType LocalAnsatzBaseFunctionSetType;

    const LocalAnsatzBaseFunctionSetType localAnsatzBaseFunctionSetEn = ansatzSpace.baseFunctionSet().local(entity);

    typedef typename TestSpaceType::BaseFunctionSetType::LocalBaseFunctionSetType LocalTesBaseFunctionSetType;

    const LocalTesBaseFunctionSetType localTestBaseFunctionSetEntity = testSpace.baseFunctionSet().local(entity);

    // check tmp local matrices
    assert(tmpLocalMatricesContainer.size() > 1);
    std::vector<LocalMatrixType>& tmpLocalMatrices = tmpLocalMatricesContainer[0];
    if (tmpLocalMatrices.size() < 4) {
      tmpLocalMatrices.resize(
          4, LocalMatrixType(ansatzSpace.map().maxLocalSize(), testSpace.map().maxLocalSize(), RangeFieldType(0.0)));
    }

    // some types
    typedef typename AnsatzSpaceType::GridPartType GridPartType;

    typedef typename GridPartType::IntersectionIteratorType IntersectionIteratorType;

    typedef typename IntersectionIteratorType::Intersection IntersectionType;

    typedef typename IntersectionType::EntityPointer EntityPointerType;

    const GridPartType& gridPart = ansatzSpace.gridPart();

    // do loop over all intersections
    for (IntersectionIteratorType intIt = gridPart.ibegin(entity); intIt != gridPart.iend(entity); ++intIt) {
      const IntersectionType& intersection = *intIt;
      // if inner intersection
      if (intersection.neighbor() && !intersection.boundary()) {
        // get neighbouring entity
        const EntityPointerType neighbourPtr = intersection.outside();
        const EntityType& neighbour          = *neighbourPtr;

        // do visit only once
        if (gridPart.indexSet().index(entity) < gridPart.indexSet().index(neighbour)) {
          // get neighbouring local basefunction sets
          const LocalAnsatzBaseFunctionSetType localAnsatzBaseFunctionSetNe =
              ansatzSpace.baseFunctionSet().local(neighbour);
          const LocalTesBaseFunctionSetType localTesBaseFunctionSetNe = testSpace.baseFunctionSet().local(neighbour);
          // apply local inner operator
          localInnerOperator_.applyLocal(localAnsatzBaseFunctionSetEn,
                                         localAnsatzBaseFunctionSetNe,
                                         localTestBaseFunctionSetEntity,
                                         localTesBaseFunctionSetNe,
                                         intersection,
                                         tmpLocalMatrices[0],
                                         tmpLocalMatrices[1],
                                         tmpLocalMatrices[2],
                                         tmpLocalMatrices[3],
                                         tmpLocalMatricesContainer[1]);

          // write local matrix to global (see below)
          addToMatrix(ansatzSpace, testSpace, entity, entity, tmpLocalMatrices[0], systemMatrix);
          addToMatrix(ansatzSpace, testSpace, entity, neighbour, tmpLocalMatrices[1], systemMatrix);
          addToMatrix(ansatzSpace, testSpace, neighbour, entity, tmpLocalMatrices[2], systemMatrix);
          addToMatrix(ansatzSpace, testSpace, neighbour, neighbour, tmpLocalMatrices[3], systemMatrix);
        } // done visit only once
      } /*else if(!intersection.neighbor() && intersection.boundary()) {
        localDirichletOperator_.applyLocal( localAnsatzBaseFunctionSetEn,
                                            localTestBaseFunctionSetEntity,
                                            intersection,
                                            tmpLocalMatrices[0],
                                            tmpLocalMatricesContainer[1] );

        addToMatrix( ansatzSpace, testSpace, entity, entity, tmpLocalMatrices[0], systemMatrix );
      }*/ // end if boundary intersection
    } // done loop over all intersections
  } // end method assembleLocal

private:
  //! assignment operator
  ThisType& operator=(const ThisType&);

  template <class AnsatzSpaceType, class TestSpaceType, class EntityType, class LocalMatrixType, class SystemMatrixType>
  void addToMatrix(const AnsatzSpaceType& ansatzSpace, const TestSpaceType& testSpace, const EntityType& ansatzEntity,
                   const EntityType& testEntity, const LocalMatrixType& localMatrix,
                   SystemMatrixType& systemMatrix) const
  {
    unsigned int rows = ansatzSpace.baseFunctionSet().local(ansatzEntity).size();
    unsigned int cols = testSpace.baseFunctionSet().local(testEntity).size();
    for (unsigned int i = 0; i < rows; ++i) {
      for (unsigned int j = 0; j < cols; ++j) {
        const unsigned int globalI = ansatzSpace.map().toGlobal(ansatzEntity, i);
        const unsigned int globalJ = testSpace.map().toGlobal(testEntity, j);

        systemMatrix.add(globalI, globalJ, localMatrix[i][j]);
      }
    }
  } // end method addToMatrix

  const LocalInnerOperatorType& localInnerOperator_;
  //  const LocalDirichletOperatorType localDirichletOperator_;
  //  const LocalNeumannOperatorType localNeumannOperator;

}; // end class Matrix

} // end namespace Codim1

} // end namespace Local

} // end namespace Assembler

} // namespace Discretizations

} // namespace Detailed

} // end namespace Dune

#endif // DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_MATRIX_HH
<commit_msg>[assembler.local.codim1.matrix] major recoding<commit_after>#ifndef DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_MATRIX_HH
#define DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_MATRIX_HH

// std includes
#include <vector>

// local includes
//#include "vector.hh"

namespace Dune {

namespace Detailed {

namespace Discretizations {

namespace Assembler {

namespace Local {

namespace Codim1 {

template <class LocalOperatorImp>
class Inner
{
public:
  typedef LocalOperatorImp LocalOperatorType;

  typedef Inner<LocalOperatorType> ThisType;

  typedef typename LocalOperatorType::RangeFieldType RangeFieldType;

  Inner(const LocalOperatorType& localOperator)
    : localOperator_(localOperator)
  {
  }

  const LocalOperatorType& localOperator() const
  {
    return localOperator_;
  }

private:
  static const unsigned int numTmpObjectsRequired_ = 4;

public:
  std::vector<unsigned int> numTmpObjectsRequired() const
  {
    std::vector<unsigned int> ret(2, 0);
    ret[0] = numTmpObjectsRequired_;
    ret[1] = localOperator_.numTmpObjectsRequired();
    return ret;
  } // std::vector< unsigned int > numTmpObjectsRequired() const

  template <class IntersectionType, class InnerAnsatzSpaceType, class InnerTestSpaceType, class OuterAnsatzSpaceType,
            class OuterTestSpaceType, class MatrixBackendType, class LocalMatrixType>
  void assembleLocal(const IntersectionType& intersection, const InnerAnsatzSpaceType& innerAnsatzSpace,
                     const InnerTestSpaceType& innerTestSpace, const OuterAnsatzSpaceType& outerAnsatzSpace,
                     const OuterTestSpaceType& outerTestSpace, MatrixBackendType& innerInnerMatrix,
                     MatrixBackendType& outerOuterMatrix, MatrixBackendType& innerOuterMatrix,
                     MatrixBackendType& outerInnerMatrix,
                     std::vector<std::vector<LocalMatrixType>>& tmpLocalMatricesContainer) const
  {
    // preparations
    assert(intersection.neighbor() && !intersection.boundary());
    typedef typename IntersectionType::EntityPointer EntityPointerType;
    typedef typename IntersectionType::Entity EntityType;
    typedef typename InnerAnsatzSpaceType::BaseFunctionSetType::LocalBaseFunctionSetType InnerAnsatzBaseFunctionSetType;
    typedef typename InnerTestSpaceType::BaseFunctionSetType::LocalBaseFunctionSetType InnerTestBaseFunctionSetType;
    typedef typename OuterAnsatzSpaceType::BaseFunctionSetType::LocalBaseFunctionSetType OuterAnsatzBaseFunctionSetType;
    typedef typename OuterTestSpaceType::BaseFunctionSetType::LocalBaseFunctionSetType OuterTestBaseFunctionSetType;
    // get inside entity and basefunctionsets
    const EntityPointerType insideEntityPtr = intersection.inside();
    const EntityType& insideEntity = *insideEntityPtr;
    const InnerAnsatzBaseFunctionSetType innerAnsatzBaseFunctionSet =
        innerAnsatzSpace.baseFunctionSet().local(insideEntity);
    const InnerTestBaseFunctionSetType innerTestBaseFunctionSet = innerTestSpace.baseFunctionSet().local(insideEntity);
    // get outside neighbor and basefunctionsets
    const EntityPointerType outsideNeighborPtr = intersection.outside();
    const EntityType& outsideNeighbor = *outsideNeighborPtr;
    const OuterAnsatzBaseFunctionSetType outerAnsatzBaseFunctionSet =
        outerAnsatzSpace.baseFunctionSet().local(outsideNeighbor);
    const OuterTestBaseFunctionSetType outerTestBaseFunctionSet =
        outerTestSpace.baseFunctionSet().local(outsideNeighbor);
    // ensure enough tmp local matrices
    assert(tmpLocalMatricesContainer.size() > 1);
    std::vector<LocalMatrixType>& tmpLocalMatrices = tmpLocalMatricesContainer[0];
    if (tmpLocalMatrices.size() < numTmpObjectsRequired_) {
      tmpLocalMatrices.resize(
          numTmpObjectsRequired_,
          LocalMatrixType(std::max(innerAnsatzSpace.map().maxLocalSize(), outerAnsatzSpace.map().maxLocalSize()),
                          std::max(innerTestSpace.map().maxLocalSize(), outerTestSpace.map().maxLocalSize()),
                          RangeFieldType(0.0)));
    } // ensure enough tmp local matrices
    // apply local operator
    localOperator_.applyLocal(innerAnsatzBaseFunctionSet,
                              innerTestBaseFunctionSet,
                              outerAnsatzBaseFunctionSet,
                              outerTestBaseFunctionSet,
                              intersection,
                              tmpLocalMatrices[0], // inside/inside
                              tmpLocalMatrices[1], // outside/outside
                              tmpLocalMatrices[2], // inside/outside
                              tmpLocalMatrices[3], // outside/inside
                              tmpLocalMatricesContainer[1]);
    // write local matrices to global (see below)
    addToMatrix(innerAnsatzSpace, innerTestSpace, insideEntity, insideEntity, tmpLocalMatrices[0], innerInnerMatrix);
    addToMatrix(
        outerAnsatzSpace, outerTestSpace, outsideNeighbor, outsideNeighbor, tmpLocalMatrices[1], outerOuterMatrix);
    addToMatrix(innerAnsatzSpace, outerTestSpace, insideEntity, outsideNeighbor, tmpLocalMatrices[2], innerOuterMatrix);
    addToMatrix(outerAnsatzSpace, innerTestSpace, outsideNeighbor, insideEntity, tmpLocalMatrices[3], outerInnerMatrix);
  } // void assembleLocal() const

private:
  //! assignment operator
  ThisType& operator=(const ThisType&);

  template <class AnsatzSpaceType, class TestSpaceType, class EntityType, class LocalMatrixType, class SystemMatrixType>
  void addToMatrix(const AnsatzSpaceType& ansatzSpace, const TestSpaceType& testSpace, const EntityType& ansatzEntity,
                   const EntityType& testEntity, const LocalMatrixType& localMatrix,
                   SystemMatrixType& systemMatrix) const
  {
    unsigned int rows = ansatzSpace.baseFunctionSet().local(ansatzEntity).size();
    unsigned int cols = testSpace.baseFunctionSet().local(testEntity).size();
    for (unsigned int i = 0; i < rows; ++i) {
      for (unsigned int j = 0; j < cols; ++j) {
        const unsigned int globalI = ansatzSpace.map().toGlobal(ansatzEntity, i);
        const unsigned int globalJ = testSpace.map().toGlobal(testEntity, j);

        systemMatrix.add(globalI, globalJ, localMatrix[i][j]);
      }
    }
  } // end method addToMatrix

  const LocalOperatorType& localOperator_;
}; // end class Inner

} // end namespace Codim1

} // end namespace Local

} // end namespace Assembler

} // namespace Discretizations

} // namespace Detailed

} // end namespace Dune

#endif // DUNE_DETAILED_DISCRETIZATIONS_ASSEMLBER_LOCAL_CODIM1_MATRIX_HH
<|endoftext|>


<commit_before>#include "SocketClient.h"

#include "arro.pb.h"
#include "Trace.h"
#include "INodeContext.h"
#include "json.hpp"

namespace Arro {

class NodeRef;
class NodeUiUserInput: public INodeDefinition {
public:
    /**
     * Constructor
     *
     * \param d The Process node instance.
     * \param name Name of this node.
     * \param params List of parameters passed to this node.
     */
    NodeUiUserInput(INodeContext* d, const std::string& name, StringMap& params, TiXmlElement*);

    virtual ~NodeUiUserInput();

    void finishConstruction();

    /**
     * Make the node execute a processing cycle.
     */
    void runCycle();

private:
    Trace m_trace;
    INodeContext* m_elemBlock;
    NodeRef* m_uiClient;
    INodeContext::ItRef m_value;
    std::string m_name;

};

} /* namespace Arro */

using namespace std;
using namespace Arro;
using namespace arro;

static RegisterMe<NodeUiUserInput> registerMe("SliderInput");

NodeUiUserInput::NodeUiUserInput(INodeContext* d, const string& /*name*/, StringMap& /*params*/, TiXmlElement*):
    m_trace{"NodeUiUserInput", true},
    m_elemBlock{d},
    m_uiClient{nullptr} {

    m_name = d->getParameter("name");

}

void
NodeUiUserInput::finishConstruction() {
    m_trace.println("finishConstruction");

    OutputPad* valuePad = m_elemBlock->getOutputPad("value");
    m_value = m_elemBlock->end(valuePad);

    std::string inst = std::string("<arro-slider id=\"") + m_elemBlock->getName() + "\" name=\"" + m_name + "\"></arro-slider>";

    m_uiClient = SocketClient::getInstance()->subscribe(m_elemBlock->getName(), inst, [=](const std::string& data) {

        Value* sel = new Value();
        auto info = nlohmann::json::parse(data.c_str());
        int value = info["value"];
        sel->set_value(value);
        m_value->setRecord(*sel);
                    });
}

NodeUiUserInput::~NodeUiUserInput() {
    SocketClient::getInstance()->unsubscribe(m_uiClient);
}

void NodeUiUserInput::runCycle() {
}

<commit_msg>Add parameters to UiUserInput<commit_after>#include "SocketClient.h"

#include "arro.pb.h"
#include "Trace.h"
#include "INodeContext.h"
#include "json.hpp"

namespace Arro {

class NodeRef;
class NodeUiUserInput: public INodeDefinition {
public:
    /**
     * Constructor
     *
     * \param d The Process node instance.
     * \param name Name of this node.
     * \param params List of parameters passed to this node.
     */
    NodeUiUserInput(INodeContext* d, const std::string& name, StringMap& params, TiXmlElement*);

    virtual ~NodeUiUserInput();

    void finishConstruction();

    /**
     * Make the node execute a processing cycle.
     */
    void runCycle();

private:
    Trace m_trace;
    INodeContext* m_elemBlock;
    NodeRef* m_uiClient;
    INodeContext::ItRef m_value;
    std::string m_name;
    int m_bot;
    int m_top;

};

} /* namespace Arro */

using namespace std;
using namespace Arro;
using namespace arro;

static RegisterMe<NodeUiUserInput> registerMe("SliderInput");

NodeUiUserInput::NodeUiUserInput(INodeContext* d, const string& /*name*/, StringMap& /*params*/, TiXmlElement*):
    m_trace{"NodeUiUserInput", true},
    m_elemBlock{d},
    m_uiClient{nullptr} {

        m_name = d->getParameter("name");
        m_bot = std::stoi(d->getParameter("botValue"));
        m_top = std::stoi(d->getParameter("topValue"));

}

void
NodeUiUserInput::finishConstruction() {
    m_trace.println("finishConstruction");

    OutputPad* valuePad = m_elemBlock->getOutputPad("value");
    m_value = m_elemBlock->end(valuePad);

    std::string inst = std::string("<arro-slider id=\"") + m_elemBlock->getName() + "\" name=\"" + m_name + "\"></arro-slider>";

    m_uiClient = SocketClient::getInstance()->subscribe(m_elemBlock->getName(), inst, [=](const std::string& data) {

        Value* sel = new Value();
        auto info = nlohmann::json::parse(data.c_str());
        int value = info["value"];

        // Value is 0..100, convert to bot..top
        value =  (((float)value / 100) * (m_top - m_bot)) + m_bot;

        sel->set_value(value);
        m_value->setRecord(*sel);
                    });
}

NodeUiUserInput::~NodeUiUserInput() {
    SocketClient::getInstance()->unsubscribe(m_uiClient);
}

void NodeUiUserInput::runCycle() {
}

<|endoftext|>


<commit_before>// This is sometimes required to support the full curses API
#define _XOPEN_SOURCE
#define _XOPEN_SOURCE_EXTENDED

#ifdef __GNUC__
	// Enable printf and scanf argument checking
	#define GCC_PRINTF
	#define	GCC_SCANF
#endif

#include <curses.h>
#include <climits>
#include <cstdio>
#include <unordered_map>
#include <cwctype>

#include <QString>
#include <QTextBoundaryFinder>

#include "lirch_constants.h"
#include "plugins/lirch_plugin.h"
#include "plugins/display_messages.h"
#include "plugins/notify_messages.h"
#include "plugins/edict_messages.h"
#include "plugins/channel_messages.h"
#include "ui/line_editor.h"
#include "core/core_messages.h"
#include "plugins/QString_hash.h"

inline char CTRL(char c)
{
	//Leave only the lower 5 bits, so the return value is the numeric value
	//of CTRL+key (e.g. CTRL('c') gives you 3, which is the value you get
	//when hitting ctrl-c)
	return c&0x1f;
}

using namespace std;

template <class... args>
string strprintf(const string &format, args... a)
{
	//Thankfully, the C++ standard grabbed the definition from POSIX
	//instead of Windows, so I don't have to binary-search the correct
	//string size.
	int size=snprintf(NULL, 0, format.c_str(), a...);
	//Add padding for the terminating byte
	vector<char> s(size+1);
	//We can use sprintf instead of snprintf because we know the buffer is large enough
	sprintf(s.data(), format.c_str(), a...);
	return s.data();
}

//Same as wprintf, but handles unicode characters properly
template <class... args>
void wprintu(WINDOW *w, const string &format, args... a)
{
	string s=strprintf(format, a...);
	wstring wstr=QString::fromLocal8Bit(s.c_str()).toStdWString();
	waddwstr(w, wstr.c_str());
}

//This wraps WINDOW pointers so they're be destroyed on exit
class WindowWrapper
{
public:
	WindowWrapper(WINDOW *ww) : w(ww, delwin) {}
	WindowWrapper() {int maxx, maxy; getmaxyx(stdscr, maxy, maxx); w=shared_ptr<WINDOW>(newpad(maxy-1, maxx), delwin); scrollok(get(), TRUE);}
	WINDOW *get() const {return w.get();}
	operator WINDOW*() const {return get();}
	WINDOW& operator*() const {return *w;}
	WINDOW* operator->() const {return get();}
private:
	shared_ptr<WINDOW> w;
};

void runplugin(plugin_pipe &p, const string &name)
{
	text_line input;
	QString channel="default";
	int maxx, maxy;
	getmaxyx(stdscr, maxy, maxx);
	//10000 lines of scrollback should be enough for anyone
	//WindowWrapper channel_output=newpad(10000, maxx);
	unordered_map<QString, WindowWrapper> channel_windows;
	if (channel_windows["default"]==nullptr)
		return;
	WindowWrapper input_display=newwin(1, maxx-1, maxy-1, 0);
	if (input_display==nullptr)
		return;
	//Be lazy and let the input scroll
	scrollok(input_display, TRUE);
	//p.write(registration_message::create(-30000, name, "display"));
	while (true)
	{
		wint_t key;
		int rc=get_wch(&key);
		if (rc==OK)
		{
			if (key=='\r' || key=='\n')
			{
				p.write(raw_edict_message::create(input.getQString(),channel));
				input.kill_whole_line();
			}
			else if (key==(unsigned char)CTRL('U'))
			{
				input.backward_kill_line();
			}
			else if (key==(unsigned char)CTRL('H'))
			{
				input.backward_delete_char();
			}
			else if (key==WEOF)
			{
				//Quit since we have no more input
				break;
			}
			else
			{
				//This conversion is always valid, since key's
				//only valid values are valid wchar_t values
				//and WEOF, and we know it's not WEOF.
				input.insert(key);
			}
		}
		else if (rc==KEY_CODE_YES)
		{
			if (key==KEY_BACKSPACE)
			{
				input.backward_delete_char();
			}
			else if (key==KEY_ENTER)
			{
				p.write(raw_edict_message::create(input.getQString(),channel));
				input.kill_whole_line();
			}
		}
		while (p.has_message())
		{
			message m=p.read();
			if (m.type=="shutdown")
			{
				return;
			}
			else if (m.type=="registration_status")
			{
				auto s=dynamic_cast<registration_status *>(m.getdata());
				if (!s)
					continue;
				if (!s->status)
				{
					if (s->priority>-32000)
						p.write(registration_message::create(s->priority-1, name, s->type));
				}
			}
			else if (m.type=="display")
			{
				auto s=dynamic_cast<display_message *>(m.getdata());
				if (!s)
					continue;
				p.write(m.decrement_priority());

				QString message_channel=s->channel;
				string nick=s->nick.toLocal8Bit().constData();
				string contents=s->contents.toLocal8Bit().constData();

				if (s->channel=="")
					message_channel=channel;

				if (channel_windows.count(message_channel)!=0)
				{
					if(s->subtype==display_message_subtype::NORMAL)
						wprintu(channel_windows[message_channel], "<%s> %s\n", nick.c_str(), contents.c_str());
					if(s->subtype==display_message_subtype::ME)
						wprintu(channel_windows[message_channel], "* %s %s\n", nick.c_str(), contents.c_str());
					if(s->subtype==display_message_subtype::NOTIFY)
						wprintu(channel_windows[message_channel], QString((QChar[]){0x203C, 0x203D, ' ', '%', 's', '\n', 0}).toLocal8Bit().constData(), contents.c_str());
					if(s->subtype==display_message_subtype::NOTIFY_CURRENT)
						wprintu(channel_windows[message_channel], QString((QChar[]){0x203C, 0x203C, 0x203D, ' ', '%', 's', '\n', 0}).toLocal8Bit().constData(), contents.c_str());
				}
			}
			else if (m.type=="set_channel")
			{
				auto i=dynamic_cast<set_channel_message *>(m.getdata());
				if (!i)
					continue;
				p.write(m.decrement_priority());
				if (i->channel=="")
					p.write(notify_message::create(channel, "On channel "+channel));
				else
					channel=i->channel;
			}
			else if (m.type=="leave_channel")
			{
				auto i=dynamic_cast<leave_channel_message *>(m.getdata());
				if (!i)
					continue;
				p.write(m.decrement_priority());
				channel_windows.erase(i->channel);
				if (i->channel==channel)
					p.write(set_channel_message::create(channel));
			}
			else
				p.write(m.decrement_priority());
		}
		int x,y;
		getyx(channel_windows[channel], y, x);
		pnoutrefresh(channel_windows[channel], max(y-(maxy-1),0), 0, 0,0,maxy-2,maxx-1);
		wmove(input_display, 0, 0);
		wprintu(input_display, "\n%s", input.getQString().toLocal8Bit().constData());
		wnoutrefresh(input_display);
		//This doesn't need to run all the time, but we should be able
		//to cope with the screen refreshing at 10Hz
		doupdate();
	}
	p.write(core_quit_message::create());
}

void run(plugin_pipe p, string name)
{
	p.write(registration_message::create(-30000, name, "display"));
	p.write(registration_message::create(-30000, name, "set_channel"));
	p.write(registration_message::create(-30000, name, "leave_channel"));
	//Set the delay when hitting escape to 10 milliseconds, unless it was
	//already set.  The ESCDELAY variable is not supported in all curses
	//implementations, but should not cause problems in implementations
	//that ignore it.
	setenv("ESCDELAY", "10", 0);
	//Initialize curses
	initscr();
	//Don't buffer typed characters
	cbreak();
	//Wain no more than a tenth of a second for input
	timeout(100);
	noecho();
	//Makes enter return \r instead of \n
	nonl();
	//Flush the input buffer if an interrupt key is pressed.  This ensures
	//we don't miss keystrokes in the event of a SIGSTOP
	intrflush(stdscr, FALSE);
	//Enable keycodes
	keypad(stdscr, TRUE);
	runplugin(p, name);
	//Make sure to always restore the terminal to a sane configuration
	endwin();
	return;
}
<commit_msg>Made '/leave' leave the current channel<commit_after>// This is sometimes required to support the full curses API
#define _XOPEN_SOURCE
#define _XOPEN_SOURCE_EXTENDED

#ifdef __GNUC__
	// Enable printf and scanf argument checking
	#define GCC_PRINTF
	#define	GCC_SCANF
#endif

#include <curses.h>
#include <climits>
#include <cstdio>
#include <unordered_map>
#include <cwctype>

#include <QString>
#include <QTextBoundaryFinder>

#include "lirch_constants.h"
#include "plugins/lirch_plugin.h"
#include "plugins/display_messages.h"
#include "plugins/notify_messages.h"
#include "plugins/edict_messages.h"
#include "plugins/channel_messages.h"
#include "ui/line_editor.h"
#include "core/core_messages.h"
#include "plugins/QString_hash.h"

inline char CTRL(char c)
{
	//Leave only the lower 5 bits, so the return value is the numeric value
	//of CTRL+key (e.g. CTRL('c') gives you 3, which is the value you get
	//when hitting ctrl-c)
	return c&0x1f;
}

using namespace std;

template <class... args>
string strprintf(const string &format, args... a)
{
	//Thankfully, the C++ standard grabbed the definition from POSIX
	//instead of Windows, so I don't have to binary-search the correct
	//string size.
	int size=snprintf(NULL, 0, format.c_str(), a...);
	//Add padding for the terminating byte
	vector<char> s(size+1);
	//We can use sprintf instead of snprintf because we know the buffer is large enough
	sprintf(s.data(), format.c_str(), a...);
	return s.data();
}

//Same as wprintf, but handles unicode characters properly
template <class... args>
void wprintu(WINDOW *w, const string &format, args... a)
{
	string s=strprintf(format, a...);
	wstring wstr=QString::fromLocal8Bit(s.c_str()).toStdWString();
	waddwstr(w, wstr.c_str());
}

//This wraps WINDOW pointers so they're be destroyed on exit
class WindowWrapper
{
public:
	WindowWrapper(WINDOW *ww) : w(ww, delwin) {}
	WindowWrapper() {int maxx, maxy; getmaxyx(stdscr, maxy, maxx); w=shared_ptr<WINDOW>(newpad(maxy-1, maxx), delwin); scrollok(get(), TRUE);}
	WINDOW *get() const {return w.get();}
	operator WINDOW*() const {return get();}
	WINDOW& operator*() const {return *w;}
	WINDOW* operator->() const {return get();}
private:
	shared_ptr<WINDOW> w;
};

void runplugin(plugin_pipe &p, const string &name)
{
	text_line input;
	QString channel="default";
	int maxx, maxy;
	getmaxyx(stdscr, maxy, maxx);
	//10000 lines of scrollback should be enough for anyone
	//WindowWrapper channel_output=newpad(10000, maxx);
	unordered_map<QString, WindowWrapper> channel_windows;
	if (channel_windows["default"]==nullptr)
		return;
	WindowWrapper input_display=newwin(1, maxx-1, maxy-1, 0);
	if (input_display==nullptr)
		return;
	//Be lazy and let the input scroll
	scrollok(input_display, TRUE);
	//p.write(registration_message::create(-30000, name, "display"));
	while (true)
	{
		wint_t key;
		int rc=get_wch(&key);
		if (rc==OK)
		{
			if (key=='\r' || key=='\n')
			{
				p.write(raw_edict_message::create(input.getQString(),channel));
				input.kill_whole_line();
			}
			else if (key==(unsigned char)CTRL('U'))
			{
				input.backward_kill_line();
			}
			else if (key==(unsigned char)CTRL('H'))
			{
				input.backward_delete_char();
			}
			else if (key==WEOF)
			{
				//Quit since we have no more input
				break;
			}
			else
			{
				//This conversion is always valid, since key's
				//only valid values are valid wchar_t values
				//and WEOF, and we know it's not WEOF.
				input.insert(key);
			}
		}
		else if (rc==KEY_CODE_YES)
		{
			if (key==KEY_BACKSPACE)
			{
				input.backward_delete_char();
			}
			else if (key==KEY_ENTER)
			{
				p.write(raw_edict_message::create(input.getQString(),channel));
				input.kill_whole_line();
			}
		}
		while (p.has_message())
		{
			message m=p.read();
			if (m.type=="shutdown")
			{
				return;
			}
			else if (m.type=="registration_status")
			{
				auto s=dynamic_cast<registration_status *>(m.getdata());
				if (!s)
					continue;
				if (!s->status)
				{
					if (s->priority>-32000)
						p.write(registration_message::create(s->priority-1, name, s->type));
				}
			}
			else if (m.type=="display")
			{
				auto s=dynamic_cast<display_message *>(m.getdata());
				if (!s)
					continue;
				p.write(m.decrement_priority());

				QString message_channel=s->channel;
				string nick=s->nick.toLocal8Bit().constData();
				string contents=s->contents.toLocal8Bit().constData();

				if (s->channel=="")
					message_channel=channel;

				if (channel_windows.count(message_channel)!=0)
				{
					if(s->subtype==display_message_subtype::NORMAL)
						wprintu(channel_windows[message_channel], "<%s> %s\n", nick.c_str(), contents.c_str());
					if(s->subtype==display_message_subtype::ME)
						wprintu(channel_windows[message_channel], "* %s %s\n", nick.c_str(), contents.c_str());
					if(s->subtype==display_message_subtype::NOTIFY)
						wprintu(channel_windows[message_channel], QString((QChar[]){0x203C, 0x203D, ' ', '%', 's', '\n', 0}).toLocal8Bit().constData(), contents.c_str());
					if(s->subtype==display_message_subtype::NOTIFY_CURRENT)
						wprintu(channel_windows[message_channel], QString((QChar[]){0x203C, 0x203C, 0x203D, ' ', '%', 's', '\n', 0}).toLocal8Bit().constData(), contents.c_str());
				}
			}
			else if (m.type=="set_channel")
			{
				auto i=dynamic_cast<set_channel_message *>(m.getdata());
				if (!i)
					continue;
				p.write(m.decrement_priority());
				if (i->channel=="")
					p.write(notify_message::create(channel, "On channel "+channel));
				else
					channel=i->channel;
			}
			else if (m.type=="leave_channel")
			{
				auto i=dynamic_cast<leave_channel_message *>(m.getdata());
				if (!i)
					continue;
				p.write(m.decrement_priority());
				if (i->channel=="")
					i->channel=channel;
				channel_windows.erase(i->channel);
				if (i->channel==channel)
					p.write(set_channel_message::create(channel));
			}
			else
				p.write(m.decrement_priority());
		}
		int x,y;
		getyx(channel_windows[channel], y, x);
		pnoutrefresh(channel_windows[channel], max(y-(maxy-1),0), 0, 0,0,maxy-2,maxx-1);
		wmove(input_display, 0, 0);
		wprintu(input_display, "\n%s", input.getQString().toLocal8Bit().constData());
		wnoutrefresh(input_display);
		//This doesn't need to run all the time, but we should be able
		//to cope with the screen refreshing at 10Hz
		doupdate();
	}
	p.write(core_quit_message::create());
}

void run(plugin_pipe p, string name)
{
	p.write(registration_message::create(-30000, name, "display"));
	p.write(registration_message::create(-30000, name, "set_channel"));
	p.write(registration_message::create(-30000, name, "leave_channel"));
	//Set the delay when hitting escape to 10 milliseconds, unless it was
	//already set.  The ESCDELAY variable is not supported in all curses
	//implementations, but should not cause problems in implementations
	//that ignore it.
	setenv("ESCDELAY", "10", 0);
	//Initialize curses
	initscr();
	//Don't buffer typed characters
	cbreak();
	//Wain no more than a tenth of a second for input
	timeout(100);
	noecho();
	//Makes enter return \r instead of \n
	nonl();
	//Flush the input buffer if an interrupt key is pressed.  This ensures
	//we don't miss keystrokes in the event of a SIGSTOP
	intrflush(stdscr, FALSE);
	//Enable keycodes
	keypad(stdscr, TRUE);
	runplugin(p, name);
	//Make sure to always restore the terminal to a sane configuration
	endwin();
	return;
}
<|endoftext|>


<commit_before>/*
 * $Id$
 *
 * Copyright (c) 2011 Surfnet
 * Copyright (c) 2011 .SE (The Internet Infrastructure Foundation).
 * Copyright (c) 2011 OpenDNSSEC AB (svb)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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 AUTHOR ``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 AUTHOR 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 "keystate/keystate_list_task.h"
#include "shared/file.h"
#include "shared/duration.h"

#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>

#include "keystate/keystate.pb.h"
#include "xmlext-pb/xmlext-rd.h"

#include "protobuf-orm/pb-orm.h"
#include "daemon/orm.h"

#include <fcntl.h>

static const char *module_str = "keystate_list_task";

enum {KSK = 1, ZSK, CSK};
enum {KS_GEN = 0, KS_PUB, KS_RDY, KS_ACT, KS_RET, KS_DEA, KS_UNK, KS_MIX};
const char* statenames[] = {"generated", "published", "ready",
		"active", "retired", "dead", "unknown", "mixed"};
enum {DS_NSUB, DS_SUBM, DS_SBMD, DS_SEEN, DS_RETR, DS_RTRD};

/** Map 2.0 states to 1.x states
 * @param p: state of RR higher in the chain (e.g. DS)
 * @param c: state of RR lower in the chain (e.g. DNSKEY)
 * @param introducing: key goal
 * @return: state in 1.x speak
 **/
int keystate(int p, int c, int introducing)
{
	enum { HID = 0, RUM, OMN, UNR, NAV };
	if (introducing) {
		if (p == HID && c == HID) return KS_GEN;
		if (p == HID || c == HID) return KS_PUB;
		if (p == OMN && c == OMN) return KS_ACT;
		if (p == RUM || c == RUM) return KS_RDY;
		return KS_UNK;
	} else {
		if (p == HID && c == HID) return KS_DEA;
		if (p == UNR || c == UNR) return KS_RET;
		if (p == OMN && c == OMN) return KS_ACT;
		return KS_RET;
	}
}
int zskstate(const ::ods::keystate::KeyData &key)
{
	return keystate(key.dnskey().state(), key.rrsig().state(),
		key.introducing());
}
int kskstate(const ::ods::keystate::KeyData &key)
{
	return keystate(key.ds().state(), key.dnskey().state(),
		key.introducing());
}

/** Human readable keystate in 1.x speak
 * @param key: key to evaluate
 * @return: state as string
 **/
const char*
map_keystate(const ::ods::keystate::KeyData &key)
{
	int z,k;
	switch(key.role()) {
		case KSK: return statenames[kskstate(key)];
		case ZSK: return statenames[zskstate(key)];
		case CSK:
			k = kskstate(key);
			z = zskstate(key);
			if (k != z) return statenames[KS_MIX];
			return statenames[k];
	}
	statenames[KS_UNK];
}

/** Time of next transition. Caller responsible for freeing ret
 * @param zone: zone key belongs to
 * @param key: key to evaluate
 * @return: human readable transition time/event */
char*
map_keytime(::ods::keystate::EnforcerZone zone,
	const ::ods::keystate::KeyData &key)
{
	switch(key.ds_at_parent()) {
		case DS_SUBM: return strdup("waiting for ds-submit");
		case DS_SBMD: return strdup("waiting for ds-seen");
		case DS_RETR: return strdup("waiting for ds-retract");
		case DS_RTRD: return strdup("waiting for ds-gone");
	}
	if ((signed int)zone.next_change() < 0)
		return strdup("-");

	char ct[26];
	struct tm srtm;
	time_t t = (time_t)zone.next_change();
	localtime_r(&t, &srtm);
	strftime(ct, 26, "%Y-%m-%d %H:%M:%S", &srtm);
	return strdup(ct);
}

void
perform_keystate_list_compat(int sockfd, engineconfig_type* config)
{
	GOOGLE_PROTOBUF_VERIFY_VERSION;
	OrmConnRef conn;
	::ods::keystate::EnforcerZone zone;
	OrmResultRef rows;
	const char* fmt = "%-31s %-8s %-9s %-30s\n";

	if (!ods_orm_connect(sockfd, config, conn))
		return;
	OrmTransaction transaction(conn);

	if (!OrmMessageEnum(conn, zone.descriptor(), rows)) {
		ods_log_error("[%s] error enumerating zones", module_str);
		ods_printf(sockfd, "error enumerating zones\n");
		return;
	}

	ods_printf(sockfd, "Keys:\n");
	ods_printf(sockfd, fmt, "Zone:", "Keytype:", "State:",
		"Date of next transition:");

	for (bool next=OrmFirst(rows); next; next=OrmNext(rows)) {
		if (!OrmGetMessage(rows, zone, true)) {
			ods_log_error("[%s] error reading zone", module_str);
			ods_printf(sockfd, "error reading zone\n");
			return;
		}

		for (int k=0; k<zone.keys_size(); ++k) {
			const ::ods::keystate::KeyData &key = zone.keys(k);
			std::string keyrole = keyrole_Name(key.role());
			const char* state = map_keystate(key);
			char* tchange = map_keytime(zone, key);
			ods_printf(sockfd, fmt, zone.name().c_str(),
				keyrole.c_str(), state, tchange);
			free(tchange);
		}
	}
}

void
perform_keystate_list_verbose(int sockfd, engineconfig_type *config)
{
	GOOGLE_PROTOBUF_VERIFY_VERSION;

	OrmConnRef conn;
	if (!ods_orm_connect(sockfd, config, conn))
		return; // error already reported.

	{	OrmTransaction transaction(conn);
		if (!transaction.started()) {
			ods_log_error("[%s] Could not start database transaction", module_str);
			ods_printf(sockfd, "error: Could not start database transaction\n");
			return;
		}

		::ods::keystate::EnforcerZone zone;

		{	OrmResultRef rows;
			if (!OrmMessageEnum(conn, zone.descriptor(), rows)) {
				ods_log_error("[%s] error enumerating zones", module_str);
				ods_printf(sockfd, "error enumerating zones\n");
				return;
			}

			ods_printf(sockfd,
					   "Database set to: %s\n"
					   "Keys:\n"
					   "Zone:                           "
					   "Key role:     "
					   "DS:          "
					   "DNSKEY:      "
					   "RRSIGDNSKEY: "
					   "RRSIG:       "
					   "Pub: "
					   "Act: "
					   "Id:"
					   "\n"
					   ,config->datastore
					   );

			for (bool next=OrmFirst(rows); next; next=OrmNext(rows)) {

				if (!OrmGetMessage(rows, zone, true)) {
					ods_log_error("[%s] error reading zone", module_str);
					ods_printf(sockfd, "error reading zone\n");
					return;
				}

				for (int k=0; k<zone.keys_size(); ++k) {
					const ::ods::keystate::KeyData &key = zone.keys(k);
					std::string keyrole = keyrole_Name(key.role());
					std::string ds_rrstate = rrstate_Name(key.ds().state());
					std::string dnskey_rrstate = rrstate_Name(key.dnskey().state());
					std::string rrsigdnskey_rrstate = rrstate_Name(key.rrsigdnskey().state());
					std::string rrsig_rrstate = rrstate_Name(key.rrsig().state());
					ods_printf(sockfd,
							   "%-31s %-13s %-12s %-12s %-12s %-12s %d %4d    %s\n",
							   zone.name().c_str(),
							   keyrole.c_str(),
							   ds_rrstate.c_str(),
							   dnskey_rrstate.c_str(),
							   rrsigdnskey_rrstate.c_str(),
							   rrsig_rrstate.c_str(),
							   key.publish(),
							   key.active_ksk()||key.active_zsk(),
							   key.locator().c_str()
							   );
				}
			}
		}
    }
}

void
perform_keystate_list(int sockfd, engineconfig_type *config, int bverbose)
{
	if (bverbose)
		perform_keystate_list_verbose(sockfd, config);
	else
		perform_keystate_list_compat(sockfd, config);
}
<commit_msg>ditch trailing spaces<commit_after>/*
 * $Id$
 *
 * Copyright (c) 2011 Surfnet
 * Copyright (c) 2011 .SE (The Internet Infrastructure Foundation).
 * Copyright (c) 2011 OpenDNSSEC AB (svb)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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 AUTHOR ``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 AUTHOR 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 "keystate/keystate_list_task.h"
#include "shared/file.h"
#include "shared/duration.h"

#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>

#include "keystate/keystate.pb.h"
#include "xmlext-pb/xmlext-rd.h"

#include "protobuf-orm/pb-orm.h"
#include "daemon/orm.h"

#include <fcntl.h>

static const char *module_str = "keystate_list_task";

enum {KSK = 1, ZSK, CSK};
enum {KS_GEN = 0, KS_PUB, KS_RDY, KS_ACT, KS_RET, KS_DEA, KS_UNK, KS_MIX};
const char* statenames[] = {"generated", "published", "ready",
		"active", "retired", "dead", "unknown", "mixed"};
enum {DS_NSUB, DS_SUBM, DS_SBMD, DS_SEEN, DS_RETR, DS_RTRD};

/** Map 2.0 states to 1.x states
 * @param p: state of RR higher in the chain (e.g. DS)
 * @param c: state of RR lower in the chain (e.g. DNSKEY)
 * @param introducing: key goal
 * @return: state in 1.x speak
 **/
int keystate(int p, int c, int introducing)
{
	enum { HID = 0, RUM, OMN, UNR, NAV };
	if (introducing) {
		if (p == HID && c == HID) return KS_GEN;
		if (p == HID || c == HID) return KS_PUB;
		if (p == OMN && c == OMN) return KS_ACT;
		if (p == RUM || c == RUM) return KS_RDY;
		return KS_UNK;
	} else {
		if (p == HID && c == HID) return KS_DEA;
		if (p == UNR || c == UNR) return KS_RET;
		if (p == OMN && c == OMN) return KS_ACT;
		return KS_RET;
	}
}
int zskstate(const ::ods::keystate::KeyData &key)
{
	return keystate(key.dnskey().state(), key.rrsig().state(),
		key.introducing());
}
int kskstate(const ::ods::keystate::KeyData &key)
{
	return keystate(key.ds().state(), key.dnskey().state(),
		key.introducing());
}

/** Human readable keystate in 1.x speak
 * @param key: key to evaluate
 * @return: state as string
 **/
const char*
map_keystate(const ::ods::keystate::KeyData &key)
{
	int z,k;
	switch(key.role()) {
		case KSK: return statenames[kskstate(key)];
		case ZSK: return statenames[zskstate(key)];
		case CSK:
			k = kskstate(key);
			z = zskstate(key);
			if (k != z) return statenames[KS_MIX];
			return statenames[k];
	}
	statenames[KS_UNK];
}

/** Time of next transition. Caller responsible for freeing ret
 * @param zone: zone key belongs to
 * @param key: key to evaluate
 * @return: human readable transition time/event */
char*
map_keytime(::ods::keystate::EnforcerZone zone,
	const ::ods::keystate::KeyData &key)
{
	switch(key.ds_at_parent()) {
		case DS_SUBM: return strdup("waiting for ds-submit");
		case DS_SBMD: return strdup("waiting for ds-seen");
		case DS_RETR: return strdup("waiting for ds-retract");
		case DS_RTRD: return strdup("waiting for ds-gone");
	}
	if ((signed int)zone.next_change() < 0)
		return strdup("-");

	char ct[26];
	struct tm srtm;
	time_t t = (time_t)zone.next_change();
	localtime_r(&t, &srtm);
	strftime(ct, 26, "%Y-%m-%d %H:%M:%S", &srtm);
	return strdup(ct);
}

void
perform_keystate_list_compat(int sockfd, engineconfig_type* config)
{
	GOOGLE_PROTOBUF_VERIFY_VERSION;
	OrmConnRef conn;
	::ods::keystate::EnforcerZone zone;
	OrmResultRef rows;
	const char* fmt = "%-31s %-8s %-9s %s\n";

	if (!ods_orm_connect(sockfd, config, conn))
		return;
	OrmTransaction transaction(conn);

	if (!OrmMessageEnum(conn, zone.descriptor(), rows)) {
		ods_log_error("[%s] error enumerating zones", module_str);
		ods_printf(sockfd, "error enumerating zones\n");
		return;
	}

	ods_printf(sockfd, "Keys:\n");
	ods_printf(sockfd, fmt, "Zone:", "Keytype:", "State:",
		"Date of next transition:");

	for (bool next=OrmFirst(rows); next; next=OrmNext(rows)) {
		if (!OrmGetMessage(rows, zone, true)) {
			ods_log_error("[%s] error reading zone", module_str);
			ods_printf(sockfd, "error reading zone\n");
			return;
		}

		for (int k=0; k<zone.keys_size(); ++k) {
			const ::ods::keystate::KeyData &key = zone.keys(k);
			std::string keyrole = keyrole_Name(key.role());
			const char* state = map_keystate(key);
			char* tchange = map_keytime(zone, key);
			ods_printf(sockfd, fmt, zone.name().c_str(),
				keyrole.c_str(), state, tchange);
			free(tchange);
		}
	}
}

void
perform_keystate_list_verbose(int sockfd, engineconfig_type *config)
{
	GOOGLE_PROTOBUF_VERIFY_VERSION;

	OrmConnRef conn;
	if (!ods_orm_connect(sockfd, config, conn))
		return; // error already reported.

	{	OrmTransaction transaction(conn);
		if (!transaction.started()) {
			ods_log_error("[%s] Could not start database transaction", module_str);
			ods_printf(sockfd, "error: Could not start database transaction\n");
			return;
		}

		::ods::keystate::EnforcerZone zone;

		{	OrmResultRef rows;
			if (!OrmMessageEnum(conn, zone.descriptor(), rows)) {
				ods_log_error("[%s] error enumerating zones", module_str);
				ods_printf(sockfd, "error enumerating zones\n");
				return;
			}

			ods_printf(sockfd,
					   "Database set to: %s\n"
					   "Keys:\n"
					   "Zone:                           "
					   "Key role:     "
					   "DS:          "
					   "DNSKEY:      "
					   "RRSIGDNSKEY: "
					   "RRSIG:       "
					   "Pub: "
					   "Act: "
					   "Id:"
					   "\n"
					   ,config->datastore
					   );

			for (bool next=OrmFirst(rows); next; next=OrmNext(rows)) {

				if (!OrmGetMessage(rows, zone, true)) {
					ods_log_error("[%s] error reading zone", module_str);
					ods_printf(sockfd, "error reading zone\n");
					return;
				}

				for (int k=0; k<zone.keys_size(); ++k) {
					const ::ods::keystate::KeyData &key = zone.keys(k);
					std::string keyrole = keyrole_Name(key.role());
					std::string ds_rrstate = rrstate_Name(key.ds().state());
					std::string dnskey_rrstate = rrstate_Name(key.dnskey().state());
					std::string rrsigdnskey_rrstate = rrstate_Name(key.rrsigdnskey().state());
					std::string rrsig_rrstate = rrstate_Name(key.rrsig().state());
					ods_printf(sockfd,
							   "%-31s %-13s %-12s %-12s %-12s %-12s %d %4d    %s\n",
							   zone.name().c_str(),
							   keyrole.c_str(),
							   ds_rrstate.c_str(),
							   dnskey_rrstate.c_str(),
							   rrsigdnskey_rrstate.c_str(),
							   rrsig_rrstate.c_str(),
							   key.publish(),
							   key.active_ksk()||key.active_zsk(),
							   key.locator().c_str()
							   );
				}
			}
		}
    }
}

void
perform_keystate_list(int sockfd, engineconfig_type *config, int bverbose)
{
	if (bverbose)
		perform_keystate_list_verbose(sockfd, config);
	else
		perform_keystate_list_compat(sockfd, config);
}
<|endoftext|>


<commit_before>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkTrackingDeviceSource.h"

#include "mitkTrackingDevice.h"
#include "mitkTrackingTool.h"

#include "mitkIGTTimeStamp.h"
#include "mitkIGTException.h"

mitk::TrackingDeviceSource::TrackingDeviceSource()
  : mitk::NavigationDataSource(), m_TrackingDevice(NULL)
{
}

mitk::TrackingDeviceSource::~TrackingDeviceSource()
{
  if (m_TrackingDevice.IsNotNull())
  {
    if (m_TrackingDevice->GetState() == mitk::TrackingDevice::Tracking)
    {
      this->StopTracking();
    }
    if (m_TrackingDevice->GetState() == mitk::TrackingDevice::Ready)
    {
      this->Disconnect();
    }
    m_TrackingDevice = NULL;
  }
}

void mitk::TrackingDeviceSource::GenerateData()
{
  if (m_TrackingDevice.IsNull())
    return;

  if (m_TrackingDevice->GetToolCount() < 1)
    return;

  if (this->GetNumberOfIndexedOutputs() != m_TrackingDevice->GetToolCount()) // mismatch between tools and outputs. What should we do? Were tools added to the tracking device after SetTrackingDevice() was called?
  {
    //check this: TODO:
    ////this might happen if a tool is plugged into an aurora during tracking.
    //this->CreateOutputs();

    std::stringstream ss;
    ss << "mitk::TrackingDeviceSource: not enough outputs available for all tools. "
      << this->GetNumberOfOutputs() << " outputs available, but "
      << m_TrackingDevice->GetToolCount() << " tools available in the tracking device.";
    throw std::out_of_range(ss.str());
  }
  /* update outputs with tracking data from tools */
  unsigned int toolCount = m_TrackingDevice->GetToolCount();
  for (unsigned int i = 0; i < toolCount; ++i)
  {
    mitk::NavigationData* nd = this->GetOutput(i);
    assert(nd);
    mitk::TrackingTool* t = m_TrackingDevice->GetTool(i);
    assert(t);

    if ((t->IsEnabled() == false) || (t->IsDataValid() == false))
    {
      nd->SetDataValid(false);
      continue;
    }
    nd->SetDataValid(true);
    mitk::NavigationData::PositionType p;
    t->GetPosition(p);
    nd->SetPosition(p);

    mitk::NavigationData::OrientationType o;
    t->GetOrientation(o);
    nd->SetOrientation(o);
    nd->SetOrientationAccuracy(t->GetTrackingError());
    nd->SetPositionAccuracy(t->GetTrackingError());
    nd->SetIGTTimeStamp(t->GetIGTTimeStamp());

    //for backward compatibility: check if the timestamp was set, if not create a default timestamp
    if (nd->GetIGTTimeStamp()==0) nd->SetIGTTimeStamp(mitk::IGTTimeStamp::GetInstance()->GetElapsed());
  }
}

void mitk::TrackingDeviceSource::SetTrackingDevice( mitk::TrackingDevice* td )
{
  MITK_DEBUG << "Setting TrackingDevice to " << td;
  if (this->m_TrackingDevice.GetPointer() != td)
  {
    this->m_TrackingDevice = td;
    this->CreateOutputs();
    std::stringstream name; // create a human readable name for the source
    name << td->GetData().Model << " Tracking Source";
    this->SetName(name.str());
  }
}

void mitk::TrackingDeviceSource::CreateOutputs(){
  //if outputs are set then delete them
  if (this->GetNumberOfOutputs() > 0)
  {
    for (unsigned int numOP = this->GetNumberOfOutputs() -1; numOP >= 0; numOP--)
      this->RemoveOutput(this->GetOutput(numOP));
    this->Modified();
  }

  //fill the outputs if a valid tracking device is set
  if (m_TrackingDevice.IsNull())
    return;

  this->SetNumberOfIndexedOutputs(m_TrackingDevice->GetToolCount());  // create outputs for all tools
  unsigned int numberOfOutputs = this->GetNumberOfIndexedOutputs();
  MITK_DEBUG << "Number of tools at start of method CreateOutputs(): " << m_TrackingDevice->GetToolCount();
  MITK_DEBUG << "Number of outputs at start of method CreateOutputs(): " << numberOfOutputs;
  for (unsigned int idx = 0; idx < m_TrackingDevice->GetToolCount(); ++idx)
  {
    if (this->GetOutput(idx) == NULL)
    {
      DataObjectPointer newOutput = this->MakeOutput(idx);
      static_cast<mitk::NavigationData*>(newOutput.GetPointer())->SetName(m_TrackingDevice->GetTool(idx)->GetToolName()); // set NavigationData name to ToolName
      this->SetNthOutput(idx, newOutput);
      this->Modified();
    }
  }
}

void mitk::TrackingDeviceSource::Connect()
{
  if (m_TrackingDevice.IsNull())
    throw std::invalid_argument("mitk::TrackingDeviceSource: No tracking device set");
  if (this->IsConnected())
    return;
  try {m_TrackingDevice->OpenConnection();}
  catch (mitk::IGTException &e)
  {
    throw std::runtime_error(std::string("mitk::TrackingDeviceSource: Could not open connection to tracking device. Error: ") + e.GetDescription());
  }

  /* NDI Aurora needs a connection to discover tools that are connected to it.
  Therefore we need to create outputs for these tools now */
  //if (m_TrackingDevice->GetType() == mitk::NDIAurora)
  //this->CreateOutputs();
}

void mitk::TrackingDeviceSource::StartTracking()
{
  if (m_TrackingDevice.IsNull())
    throw std::invalid_argument("mitk::TrackingDeviceSource: No tracking device set");
  if (m_TrackingDevice->GetState() == mitk::TrackingDevice::Tracking)
    return;
  if (m_TrackingDevice->StartTracking() == false)
    throw std::runtime_error("mitk::TrackingDeviceSource: Could not start tracking");
}

void mitk::TrackingDeviceSource::Disconnect()
{
  if (m_TrackingDevice.IsNull())
    throw std::invalid_argument("mitk::TrackingDeviceSource: No tracking device set");
  if (m_TrackingDevice->CloseConnection() == false)
    throw std::runtime_error("mitk::TrackingDeviceSource: Could not close connection to tracking device");
}

void mitk::TrackingDeviceSource::StopTracking()
{
  if (m_TrackingDevice.IsNull())
    throw std::invalid_argument("mitk::TrackingDeviceSource: No tracking device set");
  if (m_TrackingDevice->StopTracking() == false)
    throw std::runtime_error("mitk::TrackingDeviceSource: Could not stop tracking");
}

void mitk::TrackingDeviceSource::UpdateOutputInformation()
{
  if(this->GetTrackingDevice()->GetToolCount() != this->GetNumberOfIndexedOutputs())
    this->CreateOutputs();

  this->Modified();  // make sure that we need to be updated
  Superclass::UpdateOutputInformation();
}

//unsigned int mitk::TrackingDeviceSource::GetToolCount()
//{
//  if (m_TrackingDevice)
//    return m_TrackingDevice->GetToolCount();
//  return 0;
//}

bool mitk::TrackingDeviceSource::IsConnected()
{
  if (m_TrackingDevice.IsNull())
    return false;

  return (m_TrackingDevice->GetState() == mitk::TrackingDevice::Ready) || (m_TrackingDevice->GetState() == mitk::TrackingDevice::Tracking);
}

bool mitk::TrackingDeviceSource::IsTracking()
{
  if (m_TrackingDevice.IsNull())
    return false;

  return m_TrackingDevice->GetState() == mitk::TrackingDevice::Tracking;
}<commit_msg>COMP: Changed unsigned int to int, now avoiding endless for loop<commit_after>/*===================================================================

The Medical Imaging Interaction Toolkit (MITK)

Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.

This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

See LICENSE.txt or http://www.mitk.org for details.

===================================================================*/

#include "mitkTrackingDeviceSource.h"

#include "mitkTrackingDevice.h"
#include "mitkTrackingTool.h"

#include "mitkIGTTimeStamp.h"
#include "mitkIGTException.h"

mitk::TrackingDeviceSource::TrackingDeviceSource()
  : mitk::NavigationDataSource(), m_TrackingDevice(NULL)
{
}

mitk::TrackingDeviceSource::~TrackingDeviceSource()
{
  if (m_TrackingDevice.IsNotNull())
  {
    if (m_TrackingDevice->GetState() == mitk::TrackingDevice::Tracking)
    {
      this->StopTracking();
    }
    if (m_TrackingDevice->GetState() == mitk::TrackingDevice::Ready)
    {
      this->Disconnect();
    }
    m_TrackingDevice = NULL;
  }
}

void mitk::TrackingDeviceSource::GenerateData()
{
  if (m_TrackingDevice.IsNull())
    return;

  if (m_TrackingDevice->GetToolCount() < 1)
    return;

  if (this->GetNumberOfIndexedOutputs() != m_TrackingDevice->GetToolCount()) // mismatch between tools and outputs. What should we do? Were tools added to the tracking device after SetTrackingDevice() was called?
  {
    //check this: TODO:
    ////this might happen if a tool is plugged into an aurora during tracking.
    //this->CreateOutputs();

    std::stringstream ss;
    ss << "mitk::TrackingDeviceSource: not enough outputs available for all tools. "
      << this->GetNumberOfOutputs() << " outputs available, but "
      << m_TrackingDevice->GetToolCount() << " tools available in the tracking device.";
    throw std::out_of_range(ss.str());
  }
  /* update outputs with tracking data from tools */
  unsigned int toolCount = m_TrackingDevice->GetToolCount();
  for (unsigned int i = 0; i < toolCount; ++i)
  {
    mitk::NavigationData* nd = this->GetOutput(i);
    assert(nd);
    mitk::TrackingTool* t = m_TrackingDevice->GetTool(i);
    assert(t);

    if ((t->IsEnabled() == false) || (t->IsDataValid() == false))
    {
      nd->SetDataValid(false);
      continue;
    }
    nd->SetDataValid(true);
    mitk::NavigationData::PositionType p;
    t->GetPosition(p);
    nd->SetPosition(p);

    mitk::NavigationData::OrientationType o;
    t->GetOrientation(o);
    nd->SetOrientation(o);
    nd->SetOrientationAccuracy(t->GetTrackingError());
    nd->SetPositionAccuracy(t->GetTrackingError());
    nd->SetIGTTimeStamp(t->GetIGTTimeStamp());

    //for backward compatibility: check if the timestamp was set, if not create a default timestamp
    if (nd->GetIGTTimeStamp()==0) nd->SetIGTTimeStamp(mitk::IGTTimeStamp::GetInstance()->GetElapsed());
  }
}

void mitk::TrackingDeviceSource::SetTrackingDevice( mitk::TrackingDevice* td )
{
  MITK_DEBUG << "Setting TrackingDevice to " << td;
  if (this->m_TrackingDevice.GetPointer() != td)
  {
    this->m_TrackingDevice = td;
    this->CreateOutputs();
    std::stringstream name; // create a human readable name for the source
    name << td->GetData().Model << " Tracking Source";
    this->SetName(name.str());
  }
}

void mitk::TrackingDeviceSource::CreateOutputs(){
  //if outputs are set then delete them
  if (this->GetNumberOfOutputs() > 0)
  {
    for (int numOP = this->GetNumberOfOutputs() -1; numOP >= 0; numOP--)
      this->RemoveOutput(this->GetOutput(numOP));
    this->Modified();
  }

  //fill the outputs if a valid tracking device is set
  if (m_TrackingDevice.IsNull())
    return;

  this->SetNumberOfIndexedOutputs(m_TrackingDevice->GetToolCount());  // create outputs for all tools
  unsigned int numberOfOutputs = this->GetNumberOfIndexedOutputs();
  MITK_DEBUG << "Number of tools at start of method CreateOutputs(): " << m_TrackingDevice->GetToolCount();
  MITK_DEBUG << "Number of outputs at start of method CreateOutputs(): " << numberOfOutputs;
  for (unsigned int idx = 0; idx < m_TrackingDevice->GetToolCount(); ++idx)
  {
    if (this->GetOutput(idx) == NULL)
    {
      DataObjectPointer newOutput = this->MakeOutput(idx);
      static_cast<mitk::NavigationData*>(newOutput.GetPointer())->SetName(m_TrackingDevice->GetTool(idx)->GetToolName()); // set NavigationData name to ToolName
      this->SetNthOutput(idx, newOutput);
      this->Modified();
    }
  }
}

void mitk::TrackingDeviceSource::Connect()
{
  if (m_TrackingDevice.IsNull())
    throw std::invalid_argument("mitk::TrackingDeviceSource: No tracking device set");
  if (this->IsConnected())
    return;
  try {m_TrackingDevice->OpenConnection();}
  catch (mitk::IGTException &e)
  {
    throw std::runtime_error(std::string("mitk::TrackingDeviceSource: Could not open connection to tracking device. Error: ") + e.GetDescription());
  }

  /* NDI Aurora needs a connection to discover tools that are connected to it.
  Therefore we need to create outputs for these tools now */
  //if (m_TrackingDevice->GetType() == mitk::NDIAurora)
  //this->CreateOutputs();
}

void mitk::TrackingDeviceSource::StartTracking()
{
  if (m_TrackingDevice.IsNull())
    throw std::invalid_argument("mitk::TrackingDeviceSource: No tracking device set");
  if (m_TrackingDevice->GetState() == mitk::TrackingDevice::Tracking)
    return;
  if (m_TrackingDevice->StartTracking() == false)
    throw std::runtime_error("mitk::TrackingDeviceSource: Could not start tracking");
}

void mitk::TrackingDeviceSource::Disconnect()
{
  if (m_TrackingDevice.IsNull())
    throw std::invalid_argument("mitk::TrackingDeviceSource: No tracking device set");
  if (m_TrackingDevice->CloseConnection() == false)
    throw std::runtime_error("mitk::TrackingDeviceSource: Could not close connection to tracking device");
}

void mitk::TrackingDeviceSource::StopTracking()
{
  if (m_TrackingDevice.IsNull())
    throw std::invalid_argument("mitk::TrackingDeviceSource: No tracking device set");
  if (m_TrackingDevice->StopTracking() == false)
    throw std::runtime_error("mitk::TrackingDeviceSource: Could not stop tracking");
}

void mitk::TrackingDeviceSource::UpdateOutputInformation()
{
  if(this->GetTrackingDevice()->GetToolCount() != this->GetNumberOfIndexedOutputs())
    this->CreateOutputs();

  this->Modified();  // make sure that we need to be updated
  Superclass::UpdateOutputInformation();
}

//unsigned int mitk::TrackingDeviceSource::GetToolCount()
//{
//  if (m_TrackingDevice)
//    return m_TrackingDevice->GetToolCount();
//  return 0;
//}

bool mitk::TrackingDeviceSource::IsConnected()
{
  if (m_TrackingDevice.IsNull())
    return false;

  return (m_TrackingDevice->GetState() == mitk::TrackingDevice::Ready) || (m_TrackingDevice->GetState() == mitk::TrackingDevice::Tracking);
}

bool mitk::TrackingDeviceSource::IsTracking()
{
  if (m_TrackingDevice.IsNull())
    return false;

  return m_TrackingDevice->GetState() == mitk::TrackingDevice::Tracking;
}<|endoftext|>


<commit_before><commit_msg>Cosmetics<commit_after><|endoftext|>


<commit_before>#include "GSendBuffer.h"

GSendBuffer::GSendBuffer()
{
}

GSendBuffer::~GSendBuffer()
{
}
<commit_msg>Delete GSendBuffer.cpp<commit_after><|endoftext|>


<commit_before>// Adapted From https://gist.github.com/camupod/5640386
// compile using "C" linkage to avoid name obfuscation
extern "C" {
    int numInputs;
    int numOutputs;
    //constructor
    void *DSP_constructor(int samplingFreq) {
        // Make a new dsp object
        Dsp* n = new Dsp();
        // Init it with samplingFreq supplied... should we give a sample size here too?
        n->init(samplingFreq);

        return n;
    }

    int DSP_compute(Dsp *n, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
        n->compute(count, inputs, outputs);
        return 1;
    }

    int DSP_getNumInputs(Dsp *n){
        return n->getNumInputs();
    }

    int DSP_getNumOutputs(Dsp *n){
        return n->getNumOutputs();
    }

    void DSP_destructor(Dsp *n) {
        delete n;
    }
}

// Number of channels gotten from getNumInputs / getNumOutputs

// fOutChannel[i] = (float*)ioData->mBuffers[i].mData;<commit_msg>Update cpp wrapper<commit_after>// Adapted From https://gist.github.com/camupod/5640386
// compile using "C" linkage to avoid name obfuscation
#include <emscripten.h>

extern "C" {
    //constructor
    void *DSP_constructor(int samplingFreq) {

        // Make a new dsp object
        Dsp* n = new Dsp();
        // Init it with samplingFreq supplied... should we give a sample size here too?
        n->init(samplingFreq);

        return n;
    }

    int DSP_compute(Dsp *n, int count, FAUSTFLOAT** inputs, FAUSTFLOAT** outputs) {
        n->compute(count, inputs, outputs);
        return 1;
    }

    int DSP_getNumInputs(Dsp *n){
        return n->getNumInputs();
    }

    int DSP_getNumOutputs(Dsp *n){
        return n->getNumOutputs();
    }

    void DSP_destructor(Dsp *n) {
        delete n;
    }
}

// EM_ASM(
//
// );<|endoftext|>