<commit_before>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2018                                                               *
 *                                                                                *
 * 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 <modules/server/servermodule.h>

#include <modules/server/include/connection.h>
#include <modules/server/include/topics/topic.h>
#include <openspace/engine/openspaceengine.h>
#include <ghoul/fmt.h>
#include <ghoul/io/socket/socket.h>
#include <ghoul/io/socket/tcpsocketserver.h>
#include <ghoul/io/socket/websocket.h>
#include <ghoul/io/socket/websocketserver.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/templatefactory.h>

namespace {
    constexpr const char* _loggerCat = "ServerModule";
} // namespace

namespace openspace {

ServerModule::ServerModule() : OpenSpaceModule(ServerModule::Name) {}

ServerModule::~ServerModule() {
    disconnectAll();
    cleanUpFinishedThreads();
}

void ServerModule::internalInitialize(const ghoul::Dictionary&) {
    using namespace ghoul::io;

    std::unique_ptr<TcpSocketServer> tcpServer = std::make_unique<TcpSocketServer>();
    std::unique_ptr<WebSocketServer> wsServer = std::make_unique<WebSocketServer>();

    // Temporary hard coded addresses and ports.
    tcpServer->listen("localhost", 8000);
    wsServer->listen("localhost", 8001);
    LDEBUG(fmt::format(
        "TCP Server listening on {}:{}",tcpServer->address(), tcpServer->port()
    ));

    LDEBUG(fmt::format(
        "WS Server listening on {}:{}", wsServer->address(), wsServer->port()
    ));

    _servers.push_back(std::move(tcpServer));
    _servers.push_back(std::move(wsServer));

    OsEng.registerModuleCallback(
        OpenSpaceEngine::CallbackOption::PreSync,
        [this]() { preSync(); }
    );
}

void ServerModule::preSync() {
    // Set up new connections.
    for (std::unique_ptr<ghoul::io::SocketServer>& server : _servers) {
        std::unique_ptr<ghoul::io::Socket> socket;
        while ((socket = server->nextPendingSocket())) {
            socket->startStreams();
            std::shared_ptr<Connection> connection = std::make_shared<Connection>(
                std::move(socket),
                server->address()
            );
            connection->setThread(std::thread(
                [this, connection] () { handleConnection(connection); }
            ));
            _connections.push_back({ std::move(connection), false });
        }
    }

    // Consume all messages put into the message queue by the socket threads.
    consumeMessages();

    // Join threads for sockets that disconnected.
    cleanUpFinishedThreads();
}

void ServerModule::cleanUpFinishedThreads() {
    for (ConnectionData& connectionData : _connections) {
        Connection& connection = *connectionData.connection;
        if (!connection.socket() || !connection.socket()->isConnected()) {
            if (connection.thread().joinable()) {
                connection.thread().join();
                connectionData.isMarkedForRemoval = true;
            }
        }
    }
    _connections.erase(std::remove_if(
        _connections.begin(),
        _connections.end(),
        [](const ConnectionData& connectionData) {
            return connectionData.isMarkedForRemoval;
        }
    ), _connections.end());
}

void ServerModule::disconnectAll() {
    for (ConnectionData& connectionData : _connections) {
        Connection& connection = *connectionData.connection;
        if (connection.socket() && connection.socket()->isConnected()) {
            connection.socket()->disconnect(
                static_cast<int>(ghoul::io::WebSocket::ClosingReason::ClosingAll)
            );
        }
    }
}

void ServerModule::handleConnection(std::shared_ptr<Connection> connection) {
    std::string messageString;
    while (connection->socket()->getMessage(messageString)) {
        std::lock_guard<std::mutex> lock(_messageQueueMutex);
        _messageQueue.push_back({ connection, std::move(messageString) });
    }
}

void ServerModule::consumeMessages() {
    std::lock_guard<std::mutex> lock(_messageQueueMutex);
    while (!_messageQueue.empty()) {
        const Message& m = _messageQueue.front();
        _messageQueue.pop_front();
        if (std::shared_ptr<Connection> c = m.connection.lock()) {
            c->handleMessage(m.messageString);
        }
    }
}

} // namespace openspace
<commit_msg>Pop Message from queue after use to fix reference lifespan<commit_after>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2018                                                               *
 *                                                                                *
 * 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 <modules/server/servermodule.h>

#include <modules/server/include/connection.h>
#include <modules/server/include/topics/topic.h>
#include <openspace/engine/openspaceengine.h>
#include <ghoul/fmt.h>
#include <ghoul/io/socket/socket.h>
#include <ghoul/io/socket/tcpsocketserver.h>
#include <ghoul/io/socket/websocket.h>
#include <ghoul/io/socket/websocketserver.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/templatefactory.h>

namespace {
    constexpr const char* _loggerCat = "ServerModule";
} // namespace

namespace openspace {

ServerModule::ServerModule() : OpenSpaceModule(ServerModule::Name) {}

ServerModule::~ServerModule() {
    disconnectAll();
    cleanUpFinishedThreads();
}

void ServerModule::internalInitialize(const ghoul::Dictionary&) {
    using namespace ghoul::io;

    std::unique_ptr<TcpSocketServer> tcpServer = std::make_unique<TcpSocketServer>();
    std::unique_ptr<WebSocketServer> wsServer = std::make_unique<WebSocketServer>();

    // Temporary hard coded addresses and ports.
    tcpServer->listen("localhost", 8000);
    wsServer->listen("localhost", 8001);
    LDEBUG(fmt::format(
        "TCP Server listening on {}:{}",tcpServer->address(), tcpServer->port()
    ));

    LDEBUG(fmt::format(
        "WS Server listening on {}:{}", wsServer->address(), wsServer->port()
    ));

    _servers.push_back(std::move(tcpServer));
    _servers.push_back(std::move(wsServer));

    OsEng.registerModuleCallback(
        OpenSpaceEngine::CallbackOption::PreSync,
        [this]() { preSync(); }
    );
}

void ServerModule::preSync() {
    // Set up new connections.
    for (std::unique_ptr<ghoul::io::SocketServer>& server : _servers) {
        std::unique_ptr<ghoul::io::Socket> socket;
        while ((socket = server->nextPendingSocket())) {
            socket->startStreams();
            std::shared_ptr<Connection> connection = std::make_shared<Connection>(
                std::move(socket),
                server->address()
            );
            connection->setThread(std::thread(
                [this, connection] () { handleConnection(connection); }
            ));
            _connections.push_back({ std::move(connection), false });
        }
    }

    // Consume all messages put into the message queue by the socket threads.
    consumeMessages();

    // Join threads for sockets that disconnected.
    cleanUpFinishedThreads();
}

void ServerModule::cleanUpFinishedThreads() {
    for (ConnectionData& connectionData : _connections) {
        Connection& connection = *connectionData.connection;
        if (!connection.socket() || !connection.socket()->isConnected()) {
            if (connection.thread().joinable()) {
                connection.thread().join();
                connectionData.isMarkedForRemoval = true;
            }
        }
    }
    _connections.erase(std::remove_if(
        _connections.begin(),
        _connections.end(),
        [](const ConnectionData& connectionData) {
            return connectionData.isMarkedForRemoval;
        }
    ), _connections.end());
}

void ServerModule::disconnectAll() {
    for (ConnectionData& connectionData : _connections) {
        Connection& connection = *connectionData.connection;
        if (connection.socket() && connection.socket()->isConnected()) {
            connection.socket()->disconnect(
                static_cast<int>(ghoul::io::WebSocket::ClosingReason::ClosingAll)
            );
        }
    }
}

void ServerModule::handleConnection(std::shared_ptr<Connection> connection) {
    std::string messageString;
    while (connection->socket()->getMessage(messageString)) {
        std::lock_guard<std::mutex> lock(_messageQueueMutex);
        _messageQueue.push_back({ connection, std::move(messageString) });
    }
}

void ServerModule::consumeMessages() {
    std::lock_guard<std::mutex> lock(_messageQueueMutex);
    while (!_messageQueue.empty()) {
        const Message& m = _messageQueue.front();
        if (std::shared_ptr<Connection> c = m.connection.lock()) {
            c->handleMessage(m.messageString);
        }
        _messageQueue.pop_front();
    }
}

} // namespace openspace
<|endoftext|>


<commit_before>#include <vector>

#include "character.h"
#include "gamestate.h"
#include "movingentity.h"
#include "player.h"

Character::Character(Gamestate *state, PlayerPtr owner_) : MovingEntity(state), owner(owner_), pressed_keys(), held_keys()
{
    ;
}

Character::~Character()
{
    ;
}

void Character::setinput(INPUT_CONTAINER pressed_keys_, INPUT_CONTAINER held_keys_)
{
    pressed_keys = pressed_keys_;
    held_keys = held_keys_;
}

void Character::beginstep(Gamestate *state, double frametime)
{
    ;
}

void Character::midstep(Gamestate *state, double frametime)
{
    if (held_keys.LEFT)
    {
        hspeed -= 140*frametime;
    }
    if (held_keys.RIGHT)
    {
        hspeed += 140*frametime;
    }

    if (held_keys.JUMP)
    {
        vspeed -= 140*frametime;
    }
    if (held_keys.CROUCH)
    {
        vspeed += 140*frametime;
    }
}

void Character::endstep(Gamestate *state, double frametime)
{
    MovingEntity::endstep(state, frametime);

    // Collision with wallmask
    printf("\n%s", state->currentmap->collides(state, this) ? "true" : "false");
}
<commit_msg>Added working collision reaction to character.<commit_after>#include <vector>
#include <cmath>

#include "character.h"
#include "gamestate.h"
#include "movingentity.h"
#include "player.h"

Character::Character(Gamestate *state, PlayerPtr owner_) : MovingEntity(state), owner(owner_), pressed_keys(), held_keys()
{
    ;
}

Character::~Character()
{
    ;
}

void Character::setinput(INPUT_CONTAINER pressed_keys_, INPUT_CONTAINER held_keys_)
{
    pressed_keys = pressed_keys_;
    held_keys = held_keys_;
}

void Character::beginstep(Gamestate *state, double frametime)
{
    ;
}

void Character::midstep(Gamestate *state, double frametime)
{
    if (held_keys.LEFT)
    {
        hspeed -= 140*frametime;
    }
    if (held_keys.RIGHT)
    {
        hspeed += 140*frametime;
    }

    if (held_keys.JUMP)
    {
        vspeed -= 140*frametime;
    }
    if (held_keys.CROUCH)
    {
        vspeed += 140*frametime;
    }
}

void Character::endstep(Gamestate *state, double frametime)
{
    MovingEntity::endstep(state, frametime);

    // Collision with wallmask
    if (state->currentmap->collides(state, this))
    {
        double hs = hspeed*frametime, vs = vspeed*frametime;
        // We collide, do collision handling
        double oldx = x-hs, oldy = y-vs;
        // Buffers for "undone" horizontal/vertical movement
        double xbuffer = 0.0, ybuffer = 0.0;
        int steps = std::ceil(std::fmax(std::abs(hs), std::abs(vs)));
        double xstep = hs/steps, ystep = vs/steps;
        // Pull us out of the wall
        for (int i=0; i<steps; ++i)
        {
            x -= xstep; y -= ystep;
            xbuffer += xstep; ybuffer += ystep;
            if (not state->currentmap->collides(state, this))
            {
                break;
            }
        }
        // We're at the point where the character touched the wallmask for the first time
        // Now keep moving one unit in either direction until all possible movement is exhausted
        bool xblocked = false, yblocked = false;
        bool xfinished = false, yfinished = false;
        double oldxbuffer = xbuffer, oldybuffer = ybuffer;
        while (not xfinished or not yfinished)
        {
            oldxbuffer = xbuffer; oldybuffer = ybuffer;
            // Try first moving horizontally
            if (not xfinished)
            {
                x += xstep;
                if (state->currentmap->collides(state, this))
                {
                    // Doesn't work
                    x -= xstep;
                    xblocked = true;
                    xfinished = true;
                }
                else
                {
                    // It did work, deduct this from distance still to travel
                    xbuffer -= xstep;
                    if (xbuffer == 0.0 or std::signbit(xbuffer) != std::signbit(oldxbuffer))
                    {
                        // We're done for this coordinate
                        xfinished = true;
                    }
                }
            }
            // Do the same vertically
            if (not yfinished)
            {
                y += ystep;
                if (state->currentmap->collides(state, this))
                {
                    // Doesn't work
                    y -= ystep;
                    yblocked = true;
                    yfinished = true;
                }
                else
                {
                    // It did work, deduct this from distance still to travel
                    ybuffer -= ystep;
                    if (ybuffer == 0.0 or std::signbit(ybuffer) != std::signbit(oldybuffer))
                    {
                        // We're done for this coordinate
                        yfinished = true;
                    }
                }
            }
        }
        // Set hspeed and vspeed to what they should be
        if (xblocked)
        {
            hspeed = 0;
        }
        if (yblocked)
        {
            vspeed = 0;
        }
    }
}
<|endoftext|>


<commit_before>/*
 * SDR device configuration dialog
 *
 * This file is part of softrig, a simple software defined radio transceiver.
 *
 * Copyright 2017 Alexandru Csete OZ9AEC.
 *
 * 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 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 COPYRIGHT HOLDER 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 <QDebug>

#include "device_config_dialog.h"
#include "ui_device_config_dialog.h"

// Flags used to match SDR type in combo box
#define SDR_TYPE_MATCH_FLAGS    (Qt::MatchFixedString | Qt::MatchCaseSensitive)

DeviceConfigDialog::DeviceConfigDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DeviceConfigDialog)
{
    ui->setupUi(this);

    connect(ui->sdrTypeCombo, SIGNAL(currentIndexChanged(int)),
            this, SLOT(sdrTypeChanged(int)));
    connect(ui->inputRateCombo, SIGNAL(editTextChanged(QString)),
            this, SLOT(inputRateChanged(QString)));
    connect(ui->decimCombo, SIGNAL(currentIndexChanged(int)),
            this, SLOT(decimationChanged(int)));

    // The item data is the string that can be passed to the backend
    ui->sdrTypeCombo->addItem(tr("Airspy Mini"), "airspymini");
    ui->sdrTypeCombo->addItem(tr("Airspy R2"), "airspy");
    ui->sdrTypeCombo->addItem(tr("LimeSDR"), "limesdr");
//    ui->sdrTypeCombo->addItem(tr("RFSpace SDR-IQ"), "sdriq");
    ui->sdrTypeCombo->addItem(tr("RTL-SDR"), "rtlsdr");
    ui->sdrTypeCombo->addItem(tr("SDRplay"), "sdrplay");
}

DeviceConfigDialog::~DeviceConfigDialog()
{
    delete ui;
}

void DeviceConfigDialog::readSettings(const device_config_t * input)
{
    selectSdrType(input->type);
    selectSampleRate(input->rate);
    selectDecimation(input->decimation);
    setBandwidth(input->bandwidth);
}

void DeviceConfigDialog::saveSettings(device_config_t * input)
{
    input->type = ui->sdrTypeCombo->currentData(Qt::UserRole).toString();

    // QString.toInt() returns 0 if conversion fails
    input->rate = ui->inputRateCombo->currentText().toUInt();
    input->decimation = ui->decimCombo->currentText().toUInt();
    input->bandwidth = quint32(ui->bwSpinBox->value() * 1000.0);
}

void DeviceConfigDialog::sdrTypeChanged(int index)
{
    Q_UNUSED(index);

    QString     sdr_type(ui->sdrTypeCombo->currentData(Qt::UserRole).toString());

    ui->inputRateCombo->clear();
    if (sdr_type == "airspy")
    {
        ui->inputRateCombo->addItem("2500000");
        ui->inputRateCombo->addItem("10000000");
        ui->inputRateCombo->setCurrentIndex(1);
    }
    else if (sdr_type == "airspymini")
    {
        ui->inputRateCombo->addItem("3000000");
        ui->inputRateCombo->addItem("6000000");
        ui->inputRateCombo->addItem("10000000");
        ui->inputRateCombo->setCurrentIndex(1);
    }
    else if (sdr_type == "limesdr")
    {
        ui->inputRateCombo->addItem("240000");
        ui->inputRateCombo->addItem("480000");
        ui->inputRateCombo->addItem("960000");
        ui->inputRateCombo->addItem("1920000");
        ui->inputRateCombo->addItem("3840000");
        ui->inputRateCombo->addItem("7680000");
        ui->inputRateCombo->addItem("15360000");
        ui->inputRateCombo->addItem("30720000");
        ui->inputRateCombo->addItem("61440000");
    }
    else if (sdr_type == "rtlsdr")
    {
        ui->inputRateCombo->addItem("240000");
        ui->inputRateCombo->addItem("300000");
        ui->inputRateCombo->addItem("960000");
        ui->inputRateCombo->addItem("1152000");
        ui->inputRateCombo->addItem("1200000");
        ui->inputRateCombo->addItem("1536000");
        ui->inputRateCombo->addItem("1600000");
        ui->inputRateCombo->addItem("1800000");
        ui->inputRateCombo->addItem("2400000");
        ui->inputRateCombo->addItem("3200000");
        ui->inputRateCombo->setCurrentIndex(8);
    }
    else if (sdr_type == "sdriq")
    {
        ui->inputRateCombo->addItem("55556");
        ui->inputRateCombo->addItem("111111");
        ui->inputRateCombo->addItem("158730");
        ui->inputRateCombo->addItem("196078");
        ui->inputRateCombo->setCurrentIndex(3);
    }
    else if (sdr_type == "sdrplay")
    {
        ui->inputRateCombo->addItem("2000000");
        ui->inputRateCombo->addItem("4000000");
        ui->inputRateCombo->addItem("6000000");
        ui->inputRateCombo->addItem("10000000");
    }
}

void DeviceConfigDialog::inputRateChanged(const QString &rate_str)
{
    bool    conv_ok;
    int     rate;

    rate = rate_str.toInt(&conv_ok);
    if (!conv_ok || rate < 0)
        return;

    ui->decimCombo->clear();
    ui->decimCombo->addItem("None");
    // add decimations that give an integer sample rate >= 48k
    if (rate >= 96000 && rate % 2 == 0)
        ui->decimCombo->addItem("2");
    if (rate >= 192000 && rate % 4 == 0)
        ui->decimCombo->addItem("4");
    if (rate >= 384000 && rate % 8 == 0)
        ui->decimCombo->addItem("8");
    if (rate >= 768000 && rate % 16 == 0)
        ui->decimCombo->addItem("16");
    if (rate >= 1536000 && rate % 32 == 0)
        ui->decimCombo->addItem("32");
    if (rate >= 3072000 && rate % 64 == 0)
        ui->decimCombo->addItem("64");
    if (rate >= 6144000 && rate % 128 == 0)
        ui->decimCombo->addItem("128");
//    if (rate >= 12288000 && rate % 256 == 0)
//        ui->decimCombo->addItem("256");
//    if (rate >= 24576000 && rate % 512 == 0)
//        ui->decimCombo->addItem("512");

    decimationChanged(0);
}

/* New decimation rate selected.
 * Calculate the quadrature rate and update the sample rate
 * label
 */
void DeviceConfigDialog::decimationChanged(int index)
{
    Q_UNUSED(index);

    float       quad_rate;
    int         input_rate;
    int         decim;
    bool        conv_ok;

    decim = ui->decimCombo->currentText().toInt(&conv_ok);
    if (!conv_ok)
        decim = 1;

    input_rate = ui->inputRateCombo->currentText().toInt(&conv_ok);
    if (!conv_ok)
        return;

    quad_rate = float(input_rate) / float(decim);
    if (quad_rate > 1.e6f)
        ui->sampRateString->setText(QString(" %1 Msps").
                                    arg(double(quad_rate * 1.e-6f), 0, 'f', 3));
    else
        ui->sampRateString->setText(QString(" %1 ksps").
                                    arg(double(quad_rate * 1.e-3f), 0, 'f', 3));
}

// Select SDR type from type string
void DeviceConfigDialog::selectSdrType(const QString &type)
{
    int     index;

    if (type.isEmpty())
        return;

    index = ui->sdrTypeCombo->findData(type, Qt::UserRole, SDR_TYPE_MATCH_FLAGS);
    if (index >= 0)
        ui->sdrTypeCombo->setCurrentIndex(index);
}

void DeviceConfigDialog::selectSampleRate(unsigned int rate)
{
    if (rate == 0)
        return;

    ui->inputRateCombo->setCurrentText(QString("%1").arg(rate));
}

static int decim2index(unsigned int decim)
{
    int         idx;

    if (decim == 0)
        return 0;

    idx = 0;
    while (decim >>= 1)
        ++idx;

    return idx;
}

void DeviceConfigDialog::selectDecimation(unsigned int decimation)
{
    ui->decimCombo->setCurrentIndex(decim2index(decimation));
}

void DeviceConfigDialog::setBandwidth(quint32 bw)
{
    ui->bwSpinBox->setValue(double(bw) * 1.e-3);
}
<commit_msg>Update device type to be more specific<commit_after>/*
 * SDR device configuration dialog
 *
 * This file is part of softrig, a simple software defined radio transceiver.
 *
 * Copyright 2017 Alexandru Csete OZ9AEC.
 *
 * 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 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 COPYRIGHT HOLDER 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 <QDebug>

#include "device_config_dialog.h"
#include "ui_device_config_dialog.h"

// Flags used to match SDR type in combo box
#define SDR_TYPE_MATCH_FLAGS    (Qt::MatchFixedString | Qt::MatchCaseSensitive)

DeviceConfigDialog::DeviceConfigDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DeviceConfigDialog)
{
    ui->setupUi(this);

    connect(ui->sdrTypeCombo, SIGNAL(currentIndexChanged(int)),
            this, SLOT(sdrTypeChanged(int)));
    connect(ui->inputRateCombo, SIGNAL(editTextChanged(QString)),
            this, SLOT(inputRateChanged(QString)));
    connect(ui->decimCombo, SIGNAL(currentIndexChanged(int)),
            this, SLOT(decimationChanged(int)));

    // The item data is the string that can be passed to the backend
    ui->sdrTypeCombo->addItem(tr("Airspy Mini"), "airspymini");
    ui->sdrTypeCombo->addItem(tr("Airspy R2"), "airspy");
    ui->sdrTypeCombo->addItem(tr("LimeSDR Mini"), "limesdr");
//    ui->sdrTypeCombo->addItem(tr("RFSpace SDR-IQ"), "sdriq");
    ui->sdrTypeCombo->addItem(tr("RTL-SDR"), "rtlsdr");
    ui->sdrTypeCombo->addItem(tr("SDRplay RSPduo"), "sdrplay");
}

DeviceConfigDialog::~DeviceConfigDialog()
{
    delete ui;
}

void DeviceConfigDialog::readSettings(const device_config_t * input)
{
    selectSdrType(input->type);
    selectSampleRate(input->rate);
    selectDecimation(input->decimation);
    setBandwidth(input->bandwidth);
}

void DeviceConfigDialog::saveSettings(device_config_t * input)
{
    input->type = ui->sdrTypeCombo->currentData(Qt::UserRole).toString();

    // QString.toInt() returns 0 if conversion fails
    input->rate = ui->inputRateCombo->currentText().toUInt();
    input->decimation = ui->decimCombo->currentText().toUInt();
    input->bandwidth = quint32(ui->bwSpinBox->value() * 1000.0);
}

void DeviceConfigDialog::sdrTypeChanged(int index)
{
    Q_UNUSED(index);

    QString     sdr_type(ui->sdrTypeCombo->currentData(Qt::UserRole).toString());

    ui->inputRateCombo->clear();
    if (sdr_type == "airspy")
    {
        ui->inputRateCombo->addItem("2500000");
        ui->inputRateCombo->addItem("10000000");
        ui->inputRateCombo->setCurrentIndex(1);
    }
    else if (sdr_type == "airspymini")
    {
        ui->inputRateCombo->addItem("3000000");
        ui->inputRateCombo->addItem("6000000");
        ui->inputRateCombo->addItem("10000000");
        ui->inputRateCombo->setCurrentIndex(1);
    }
    else if (sdr_type == "limesdr")
    {
        ui->inputRateCombo->addItem("240000");
        ui->inputRateCombo->addItem("480000");
        ui->inputRateCombo->addItem("960000");
        ui->inputRateCombo->addItem("1920000");
        ui->inputRateCombo->addItem("3840000");
        ui->inputRateCombo->addItem("7680000");
        ui->inputRateCombo->addItem("15360000");
        ui->inputRateCombo->addItem("30720000");
        ui->inputRateCombo->addItem("61440000");
    }
    else if (sdr_type == "rtlsdr")
    {
        ui->inputRateCombo->addItem("240000");
        ui->inputRateCombo->addItem("300000");
        ui->inputRateCombo->addItem("960000");
        ui->inputRateCombo->addItem("1152000");
        ui->inputRateCombo->addItem("1200000");
        ui->inputRateCombo->addItem("1536000");
        ui->inputRateCombo->addItem("1600000");
        ui->inputRateCombo->addItem("1800000");
        ui->inputRateCombo->addItem("2400000");
        ui->inputRateCombo->addItem("3200000");
        ui->inputRateCombo->setCurrentIndex(8);
    }
    else if (sdr_type == "sdriq")
    {
        ui->inputRateCombo->addItem("55556");
        ui->inputRateCombo->addItem("111111");
        ui->inputRateCombo->addItem("158730");
        ui->inputRateCombo->addItem("196078");
        ui->inputRateCombo->setCurrentIndex(3);
    }
    else if (sdr_type == "sdrplay")
    {
        ui->inputRateCombo->addItem("2000000");
        ui->inputRateCombo->addItem("4000000");
        ui->inputRateCombo->addItem("6000000");
        ui->inputRateCombo->addItem("10000000");
    }
}

void DeviceConfigDialog::inputRateChanged(const QString &rate_str)
{
    bool    conv_ok;
    int     rate;

    rate = rate_str.toInt(&conv_ok);
    if (!conv_ok || rate < 0)
        return;

    ui->decimCombo->clear();
    ui->decimCombo->addItem("None");
    // add decimations that give an integer sample rate >= 48k
    if (rate >= 96000 && rate % 2 == 0)
        ui->decimCombo->addItem("2");
    if (rate >= 192000 && rate % 4 == 0)
        ui->decimCombo->addItem("4");
    if (rate >= 384000 && rate % 8 == 0)
        ui->decimCombo->addItem("8");
    if (rate >= 768000 && rate % 16 == 0)
        ui->decimCombo->addItem("16");
    if (rate >= 1536000 && rate % 32 == 0)
        ui->decimCombo->addItem("32");
    if (rate >= 3072000 && rate % 64 == 0)
        ui->decimCombo->addItem("64");
    if (rate >= 6144000 && rate % 128 == 0)
        ui->decimCombo->addItem("128");
//    if (rate >= 12288000 && rate % 256 == 0)
//        ui->decimCombo->addItem("256");
//    if (rate >= 24576000 && rate % 512 == 0)
//        ui->decimCombo->addItem("512");

    decimationChanged(0);
}

/* New decimation rate selected.
 * Calculate the quadrature rate and update the sample rate
 * label
 */
void DeviceConfigDialog::decimationChanged(int index)
{
    Q_UNUSED(index);

    float       quad_rate;
    int         input_rate;
    int         decim;
    bool        conv_ok;

    decim = ui->decimCombo->currentText().toInt(&conv_ok);
    if (!conv_ok)
        decim = 1;

    input_rate = ui->inputRateCombo->currentText().toInt(&conv_ok);
    if (!conv_ok)
        return;

    quad_rate = float(input_rate) / float(decim);
    if (quad_rate > 1.e6f)
        ui->sampRateString->setText(QString(" %1 Msps").
                                    arg(double(quad_rate * 1.e-6f), 0, 'f', 3));
    else
        ui->sampRateString->setText(QString(" %1 ksps").
                                    arg(double(quad_rate * 1.e-3f), 0, 'f', 3));
}

// Select SDR type from type string
void DeviceConfigDialog::selectSdrType(const QString &type)
{
    int     index;

    if (type.isEmpty())
        return;

    index = ui->sdrTypeCombo->findData(type, Qt::UserRole, SDR_TYPE_MATCH_FLAGS);
    if (index >= 0)
        ui->sdrTypeCombo->setCurrentIndex(index);
}

void DeviceConfigDialog::selectSampleRate(unsigned int rate)
{
    if (rate == 0)
        return;

    ui->inputRateCombo->setCurrentText(QString("%1").arg(rate));
}

static int decim2index(unsigned int decim)
{
    int         idx;

    if (decim == 0)
        return 0;

    idx = 0;
    while (decim >>= 1)
        ++idx;

    return idx;
}

void DeviceConfigDialog::selectDecimation(unsigned int decimation)
{
    ui->decimCombo->setCurrentIndex(decim2index(decimation));
}

void DeviceConfigDialog::setBandwidth(quint32 bw)
{
    ui->bwSpinBox->setValue(double(bw) * 1.e-3);
}
<|endoftext|>


<commit_before><commit_msg>Create 1014 - Consumption.cpp<commit_after><|endoftext|>


<commit_before>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <regex>
#include <string>
#include <vector>

#include <dlfcn.h>
#include <getopt.h>

#include "lib/picojson.h"

#include "controller.hpp"
#include "socketio.hpp"
#include "error.hpp"

using namespace processwarp;

class NativeVm : public ControllerDelegate, public SocketIoDelegate {
public:
  Controller controller;
  SocketIo socket;

  /** Configuration */
  const picojson::object conf;

  /** Device name. */
  std::string device_name;
  /** Device ID. */
  std::string device_id;

  /** Dynamic link libraries. */
  std::vector<void*> libs;

  /** Map of pid and program name. */
  std::map<std::string, std::string> procs;

  /** Map of device-id and device-name. */
  std::map<std::string, std::string> devices;

  /**
   * Constructor
   * @param _conf Configuration.
   */
  NativeVm(const picojson::object& _conf) :
    controller(*this),
    socket(*this),
    conf(_conf) {
  }

  /**
   * Load applications list on configure file and command line argument.
   */
  void load_apps() {
    if (conf.find("apps") == conf.end()) return;
    const picojson::array& apps = conf.at("apps").get<picojson::array>();

    for (auto it : apps) {
      const picojson::object& app = it.get<picojson::object>();
      // Check application type.
      if (app.find("type") != app.end() &&
	  app.at("type").get<std::string>() != "llvm") {
	throw_error_message(Error::CONFIGURE, "Unknown application type.");
      }

      // Get name from filename.
      if (app.find("file") == app.end()) {
	throw_error_message(Error::CONFIGURE, "Unrecognizable application file.");
      }
      std::string name;
      std::smatch match;
      if (regex_search(app.at("file").get<std::string>(),
		       match, std::regex("[^\\\\/]*$"))) {
	name = match.str();

      } else {
	assert(false);
      }

      // Load file.
      std::ifstream ifs(app.at("file").get<std::string>());
      if (!ifs.is_open()) {
	throw_error_message(Error::CONFIGURE, "Unrecognizable application file.");
      }
      std::stringstream file;
      std::string line;
      while (std::getline(ifs, line, '\n')) {
	file << line << '\n';
      }
      ifs.close();

      // Search device.
      std::string dst_device_id = "";
      if (app.find("device") == app.end()) {
	dst_device_id = device_id;

      } else {
	std::string device_name = app.at("device").get<std::string>();
	for (auto it : devices) {
	  if (it.second == device_name) {
	    dst_device_id = it.first;
	    break;
	  }
	}
	if (dst_device_id == "") {
	  throw_error_message(Error::CONFIGURE, "Unknown device name.");
	}
      }

      // Send request.
      socket.send_load_llvm(name, file.str(), dst_device_id);
    }
  }

  // Call when send data to other device.
  void send_warp_data(const std::string& pid,
		      const std::string& tid,
		      const std::string& dst_device_id,
		      const std::string& data) override {
    socket.send_warp_data_1(pid, tid, dst_device_id, data);
  }

  // Call when context switch of process.
  /*
  void on_switch_proccess(const std::string& pid) override {
  }
  //*/

  // Call when process was finish.
  void on_finish_proccess(const std::string& pid) override {
    procs.erase(pid);

    std::map<std::string, SocketIoProc> sio_procs;
    for (auto it : procs) {
      SocketIoProc sio_proc;
      sio_proc.pid  = it.first;
      sio_proc.name = it.second;
      sio_proc.threads.insert(make_pair("1", device_id));

      sio_procs.insert(std::make_pair(sio_proc.pid, sio_proc));
    }

    print_debug("(s)\n");
    for (auto it : sio_procs) {
      print_debug("pid:%s\n  pid:%s\n  name:%s\n",
		  it.first.c_str(), it.second.pid.c_str(), it.second.name.c_str());
      for (auto it2 : it.second.threads) {
	print_debug("    %s : %s\n", it2.first.c_str(), it2.second.c_str());
      }
    }

    socket.send_sync_proc_list(sio_procs);
  }

  // Call when rise error.
  void on_error(const std::string& pid, const std::string& message) override {
    print_debug("error(%s) : %s\n", pid.c_str(), message.c_str());
    on_finish_proccess(pid);
  }

  // Call when system error on server.
  void recv_sys_error(int code) override {
    throw_error(Error::SERVER_SYS);
  }

  // Call when application error on server.
  void recv_app_error(int code) override {
    throw_error(Error::SERVER_APP);
  }

  // Call when recv login message from server.
  void recv_login(int result) override {
    socket.send_list_device();
  }

  // Call when recv list device message from server.
  void recv_list_device(int result,
			const std::map<std::string, std::string>& devices) override {
    if (result != 0) {
      throw_error(Error::SERVER_APP);
    }

    // Keep last devices.
    this->devices = devices;

    bool is_new_device = true;
    for(auto it : devices) {
      if (it.second == device_name) {
	is_new_device = false;
	socket.send_bind_device(it.first, it.second);
	break;
      }
    }

    if (is_new_device) {
      socket.send_bind_device("", device_name);
    }
  }

  // Call when recv bind device message from server.
  void recv_bind_device(int result, const std::string& device_id) override {
    if (result != 0) {
      throw_error(Error::SERVER_APP);
    }

    this->device_id = device_id;

    // Syncronize processes empty because processes not running just run program.
    socket.send_sync_proc_list(std::map<std::string, SocketIoProc>());

    // Execute applications.
    load_apps();
  }

  // Call when recv sync proc list message from server.
  void recv_sync_proc_list(const std::map<std::string, SocketIoProc>& new_procs) override {
    print_debug("(r)\n");
    for (auto it : new_procs) {
      print_debug("pid:%s\n  pid:%s\n  name:%s\n",
		  it.first.c_str(), it.second.pid.c_str(), it.second.name.c_str());
      for (auto it2 : it.second.threads) {
	print_debug("    %s : %s\n", it2.first.c_str(), it2.second.c_str());
      }
    }

    // Check finished process.
    auto it = procs.begin();
    while (it != procs.end()) {
      if (new_procs.find(it->first) == new_procs.end()) {
	// Delete proccess if not exit in server's proccess list.
	controller.delete_process(it->first);
	it = procs.erase(it);

      } else {
	it ++;
      }
    }

    // Update process list.
    for (auto it : new_procs) {
      if (procs.find(it.first) == procs.end()) {
	procs.insert(std::make_pair(it.first, it.second.name));

      } else {
	procs.at(it.first) = it.second.name;
      }
    }
  }

  // Call when recv warp request from other device.
  void recv_warp_request_0(const std::string& pid,
			   const std::string& tid,
			   const std::string& dst_device_id,
			   const std::string& to_device_id) override {
    // Not to me.
    if (to_device_id != device_id) return;

    if (procs.find(pid) == procs.end()) return;

    socket.send_warp_request_1(pid, tid, dst_device_id);
  }

  // Call when recv warp request from device that having process.
  void recv_warp_request_1(const std::string& pid,
			   const std::string& tid,
			   const std::string& name,
			   const std::string& from_account,
			   const std::string& from_device_id,
			   const std::string& to_device_id) override {
    // Not to me.
    if (to_device_id != device_id) return;

    if (from_account == "") {
      // Accept from the same account.
      socket.send_warp_request_2(pid, tid, from_device_id, 0);
      procs.insert(std::make_pair(pid, name));
      controller.create_process(pid, libs);

    } else {
      // Deny from other account.
      socket.send_warp_request_2(pid, tid, from_device_id, -111);
    }
  }

  // Call when recv warp acception from warp destination device.
  void recv_warp_request_2(const std::string& pid,
			   const std::string& tid,
			   const std::string& from_device_id,
			   const std::string& to_device_id,
			   int result) override {
    // Not to me.
    if (to_device_id != device_id) return;

    if (result == 0) {
      controller.warp_process(pid, from_device_id);

    } else {
      fixme("Ouutput log & ignore?");
    }
  }

  // Call when recv warp data from warp source device.
  void recv_warp_data_1(const std::string& pid,
			const std::string& tid,
			const std::string& from_device_id,
			const std::string& to_device_id,
			const std::string& payload) override {
    // Not to me.
    if (to_device_id != device_id) return;

    socket.send_warp_data_2(pid,
			    tid,
			    from_device_id,
			    controller.recv_warp_data(pid, tid, payload) ? 0 : -111);
  }

  // Call when recv warp data from warp destination device.
  void recv_warp_data_2(const std::string& pid,
			const std::string& tid,
			const std::string& to_device_id,
			int result) override {
    // Do nothing.
  }

  // Call when process was killed.
  void recv_exit_process(const std::string& pid) override {
    controller.exit_process(pid);
  }

  // Recv console for test.
  void recv_test_console(const std::string& pid,
			 const std::string& dev,
			 const std::string& payload,
			 const std::string& from_device_id) {
#ifndef NDEBUG
    if (dev == "stdout") {
      std::cout << payload;
    } else {
      std::cerr << payload;
    }
#endif
  }

  void init(const picojson::object& conf) {
    // Load dynamic link libraries.
    if (conf.find("libs") != conf.end()) {
      const picojson::array& lib_paths = conf.at("libs").get<picojson::array>();
      for (auto lib : lib_paths) {
	void* dl_handle = dlopen(lib.get<std::string>().c_str(), RTLD_LAZY);
	if (!dl_handle) {
	  throw_error_message(Error::EXT_LIBRARY, dlerror());
	}
	libs.push_back(dl_handle);
      }
    }

    // Get device-name.
    device_name = conf.at("device-name").get<std::string>();

    // Connect to server by using Socket.IO.
    socket.connect(conf.at("server").get<std::string>());
    socket.send_login(conf.at("account").get<std::string>(),
		      conf.at("password").get<std::string>());
  }

  void run() {
    init(conf);
#ifndef NDEBUG
    bool is_run_app = false;
#endif

    // Main loop
    while(true) {
#ifndef NDEBUG
      // Stop program when application is not running on test.
      if(getenv("TEST") != nullptr) {
	if (procs.size() != 0) {
	  is_run_app = true;
	}
	if (is_run_app && procs.size() == 0) {
	  break;
	}
      }
#endif
      socket.pool();
      controller.loop();
    }
  }
};

/**
 * Entry point for NativeVM.
 * @return return code.
 */
int main(int argc, char* argv[]) {
  int opt, option_index;
  picojson::object conf;

  option long_options[] = {
    {"config", required_argument, nullptr, 'c'},
    {"llvm",   required_argument, nullptr, 'l'},
    {"device", required_argument, nullptr, 'd'},
    {0, 0, 0, 0} // terminate
  };

  // Analyse command line option using getopt.
  while((opt = getopt_long(argc, argv, "ac:l:d:", long_options, &option_index)) != -1) {
    switch(opt) {
    case 'c': {
      // Read configuration file.
      std::ifstream conf_file(optarg);
      if (!conf_file.is_open()) {
	std::cerr << "Can't open configure-file." << std::endl;
	return EXIT_FAILURE;
      }

      // Read as JSON formated file.
      picojson::value v;
      std::string err = picojson::parse(v, conf_file);
      conf_file.close();
      if (!err.empty()) {
	std::cerr << err << std::endl;
	goto on_error;
      }
      conf = v.get<picojson::object>();
    } break;

    case 'l': {
      // Make 'apps' directive if don't exist yet.
      if (conf.find("apps") == conf.end()) {
	conf.insert(std::make_pair("apps", picojson::value(picojson::array())));
      }
      // Make per application directive in 'apps' directive.
      picojson::object app;
      app.insert(std::make_pair("type", picojson::value(std::string("llvm"))));
      app.insert(std::make_pair("file", picojson::value(std::string(optarg))));
      app.insert(std::make_pair("args", picojson::value(picojson::array())));

      picojson::array& apps = conf.at("apps").get<picojson::array>();
      apps.push_back(picojson::value(app));
    } break;

    case 'd': {
      if (conf.find("apps") == conf.end()) goto on_error;
      picojson::array& apps = conf.at("apps").get<picojson::array>();

      for (auto& it : apps) {
	picojson::object& app = it.get<picojson::object>();
	if (app.find("device") == app.end()) {
	  app.insert(std::make_pair("device", picojson::value(std::string(optarg))));
	}
      }
    } break;

    case ':':
    case '?': {
      printf("Unknown or required argument option -%c\n", opt);
      goto on_error;
    } break;
    }
  }

  // Deal with after '--' options for last listed application.
  for (int i = optind; i < argc; i ++) {
    if (conf.find("apps") == conf.end()) goto on_error;
    picojson::array& apps = conf.at("apps").get<picojson::array>();
    if (apps.size() == 0) goto on_error;
    // Get 'arg' directive and push options.
    picojson::object& app = apps.back().get<picojson::object>();
    if (app.find("args") == app.end()) {
      app.insert(std::make_pair("args", picojson::value(picojson::array())));
    }
    picojson::array& args = app.at("args").get<picojson::array>();
    args.push_back(picojson::value(std::string(argv[i])));
  }

  { // Run.
    NativeVm native_vm(conf);
    native_vm.run();

    // Finish
    return EXIT_SUCCESS;
  }

 on_error:
  printf("Usage : COMMAND -c path [-l path [-d device] [-- ...]]\n");
  return EXIT_FAILURE;
}
<commit_msg>Update proccess infomation on local<commit_after>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <regex>
#include <string>
#include <vector>

#include <dlfcn.h>
#include <getopt.h>

#include "lib/picojson.h"

#include "controller.hpp"
#include "socketio.hpp"
#include "error.hpp"

using namespace processwarp;

class NativeVm : public ControllerDelegate, public SocketIoDelegate {
public:
  Controller controller;
  SocketIo socket;

  /** Configuration */
  const picojson::object conf;

  /** Device name. */
  std::string device_name;
  /** Device ID. */
  std::string device_id;

  /** Dynamic link libraries. */
  std::vector<void*> libs;

  /** Map of pid and process infomation. */
  std::map<std::string, SocketIoProc> procs;

  /** Map of device-id and device-name. */
  std::map<std::string, std::string> devices;

  /**
   * Constructor
   * @param _conf Configuration.
   */
  NativeVm(const picojson::object& _conf) :
    controller(*this),
    socket(*this),
    conf(_conf) {
  }

  /**
   * Load applications list on configure file and command line argument.
   */
  void load_apps() {
    if (conf.find("apps") == conf.end()) return;
    const picojson::array& apps = conf.at("apps").get<picojson::array>();

    for (auto it : apps) {
      const picojson::object& app = it.get<picojson::object>();
      // Check application type.
      if (app.find("type") != app.end() &&
	  app.at("type").get<std::string>() != "llvm") {
	throw_error_message(Error::CONFIGURE, "Unknown application type.");
      }

      // Get name from filename.
      if (app.find("file") == app.end()) {
	throw_error_message(Error::CONFIGURE, "Unrecognizable application file.");
      }
      std::string name;
      std::smatch match;
      if (regex_search(app.at("file").get<std::string>(),
		       match, std::regex("[^\\\\/]*$"))) {
	name = match.str();

      } else {
	assert(false);
      }

      // Load file.
      std::ifstream ifs(app.at("file").get<std::string>());
      if (!ifs.is_open()) {
	throw_error_message(Error::CONFIGURE, "Unrecognizable application file.");
      }
      std::stringstream file;
      std::string line;
      while (std::getline(ifs, line, '\n')) {
	file << line << '\n';
      }
      ifs.close();

      // Search device.
      std::string dst_device_id = "";
      if (app.find("device") == app.end()) {
	dst_device_id = device_id;

      } else {
	std::string device_name = app.at("device").get<std::string>();
	for (auto it : devices) {
	  if (it.second == device_name) {
	    dst_device_id = it.first;
	    break;
	  }
	}
	if (dst_device_id == "") {
	  throw_error_message(Error::CONFIGURE, "Unknown device name.");
	}
      }

      // Send request.
      socket.send_load_llvm(name, file.str(), dst_device_id);
    }
  }

  // Call when send data to other device.
  void send_warp_data(const std::string& pid,
		      const std::string& tid,
		      const std::string& dst_device_id,
		      const std::string& data) override {
    socket.send_warp_data_1(pid, tid, dst_device_id, data);
  }

  // Call when context switch of process.
  /*
  void on_switch_proccess(const std::string& pid) override {
  }
  //*/

  // Call when process was finish.
  void on_finish_proccess(const std::string& pid) override {
    procs.erase(pid);

    socket.send_sync_proc_list(procs);
  }

  // Call when rise error.
  void on_error(const std::string& pid, const std::string& message) override {
    print_debug("error(%s) : %s\n", pid.c_str(), message.c_str());
    on_finish_proccess(pid);
  }

  // Call when system error on server.
  void recv_sys_error(int code) override {
    throw_error(Error::SERVER_SYS);
  }

  // Call when application error on server.
  void recv_app_error(int code) override {
    throw_error(Error::SERVER_APP);
  }

  // Call when recv login message from server.
  void recv_login(int result) override {
    socket.send_list_device();
  }

  // Call when recv list device message from server.
  void recv_list_device(int result,
			const std::map<std::string, std::string>& devices) override {
    if (result != 0) {
      throw_error(Error::SERVER_APP);
    }

    // Keep last devices.
    this->devices = devices;

    bool is_new_device = true;
    for(auto it : devices) {
      if (it.second == device_name) {
	is_new_device = false;
	socket.send_bind_device(it.first, it.second);
	break;
      }
    }

    if (is_new_device) {
      socket.send_bind_device("", device_name);
    }
  }

  // Call when recv bind device message from server.
  void recv_bind_device(int result, const std::string& device_id) override {
    if (result != 0) {
      throw_error(Error::SERVER_APP);
    }

    this->device_id = device_id;

    // Syncronize processes empty because processes not running just run program.
    socket.send_sync_proc_list(std::map<std::string, SocketIoProc>());

    // Execute applications.
    load_apps();
  }

  // Call when recv sync proc list message from server.
  void recv_sync_proc_list(const std::map<std::string, SocketIoProc>& new_procs) override {
    // Check finished process.
    auto it = procs.begin();
    while (it != procs.end()) {
      if (new_procs.find(it->first) == new_procs.end()) {
	// Delete proccess if not exit in server's proccess list.
	controller.delete_process(it->first);
	it = procs.erase(it);

      } else {
	it ++;
      }
    }

    // Update process list.
    procs = new_procs;
  }

  // Call when recv warp request from other device.
  void recv_warp_request_0(const std::string& pid,
			   const std::string& tid,
			   const std::string& dst_device_id,
			   const std::string& to_device_id) override {
    // Not to me.
    if (to_device_id != device_id) return;

    if (procs.find(pid) == procs.end()) return;

    socket.send_warp_request_1(pid, tid, dst_device_id);
  }

  // Call when recv warp request from device that having process.
  void recv_warp_request_1(const std::string& pid,
			   const std::string& tid,
			   const std::string& name,
			   const std::string& from_account,
			   const std::string& from_device_id,
			   const std::string& to_device_id) override {
    // Not to me.
    if (to_device_id != device_id) return;

    if (from_account == "") {
      // Accept from the same account.
      socket.send_warp_request_2(pid, tid, from_device_id, 0);

      // Update procs.
      // Create new proccess infomation for new process.
      if (procs.find(pid) == procs.end()) {
	SocketIoProc proc_info;
	proc_info.pid  = pid;
	proc_info.name = name;
	procs.insert(std::make_pair(pid, proc_info));
      }
      // Update or create thread pair.
      SocketIoProc& proc_info = procs.at(pid);
      if (proc_info.threads.find(tid) != proc_info.threads.end()) {
	proc_info.threads.at(tid) = device_id;

      } else {
	proc_info.threads.insert(std::make_pair(tid, device_id));
      }

      controller.create_process(pid, libs);

    } else {
      // Deny from other account.
      socket.send_warp_request_2(pid, tid, from_device_id, -111);
    }
  }

  // Call when recv warp acception from warp destination device.
  void recv_warp_request_2(const std::string& pid,
			   const std::string& tid,
			   const std::string& from_device_id,
			   const std::string& to_device_id,
			   int result) override {
    // Not to me.
    if (to_device_id != device_id) return;

    if (result == 0) {
      controller.warp_process(pid, from_device_id);

    } else {
      fixme("Ouutput log & ignore?");
    }
  }

  // Call when recv warp data from warp source device.
  void recv_warp_data_1(const std::string& pid,
			const std::string& tid,
			const std::string& from_device_id,
			const std::string& to_device_id,
			const std::string& payload) override {
    // Not to me.
    if (to_device_id != device_id) return;

    socket.send_warp_data_2(pid,
			    tid,
			    from_device_id,
			    controller.recv_warp_data(pid, tid, payload) ? 0 : -111);
  }

  // Call when recv warp data from warp destination device.
  void recv_warp_data_2(const std::string& pid,
			const std::string& tid,
			const std::string& to_device_id,
			int result) override {
    // Do nothing.
  }

  // Call when process was killed.
  void recv_exit_process(const std::string& pid) override {
    controller.exit_process(pid);
  }

  // Recv console for test.
  void recv_test_console(const std::string& pid,
			 const std::string& dev,
			 const std::string& payload,
			 const std::string& from_device_id) {
#ifndef NDEBUG
    if (dev == "stdout") {
      std::cout << payload;
    } else {
      std::cerr << payload;
    }
#endif
  }

  void init(const picojson::object& conf) {
    // Load dynamic link libraries.
    if (conf.find("libs") != conf.end()) {
      const picojson::array& lib_paths = conf.at("libs").get<picojson::array>();
      for (auto lib : lib_paths) {
	void* dl_handle = dlopen(lib.get<std::string>().c_str(), RTLD_LAZY);
	if (!dl_handle) {
	  throw_error_message(Error::EXT_LIBRARY, dlerror());
	}
	libs.push_back(dl_handle);
      }
    }

    // Get device-name.
    device_name = conf.at("device-name").get<std::string>();

    // Connect to server by using Socket.IO.
    socket.connect(conf.at("server").get<std::string>());
    socket.send_login(conf.at("account").get<std::string>(),
		      conf.at("password").get<std::string>());
  }

  void run() {
    init(conf);
#ifndef NDEBUG
    bool is_run_app = false;
#endif

    // Main loop
    while(true) {
#ifndef NDEBUG
      // Stop program when application is not running on test.
      if(getenv("TEST") != nullptr) {
	if (procs.size() != 0) {
	  is_run_app = true;
	}
	if (is_run_app && procs.size() == 0) {
	  break;
	}
      }
#endif
      socket.pool();
      controller.loop();
    }
  }
};

/**
 * Entry point for NativeVM.
 * @return return code.
 */
int main(int argc, char* argv[]) {
  int opt, option_index;
  picojson::object conf;

  option long_options[] = {
    {"config", required_argument, nullptr, 'c'},
    {"llvm",   required_argument, nullptr, 'l'},
    {"device", required_argument, nullptr, 'd'},
    {0, 0, 0, 0} // terminate
  };

  // Analyse command line option using getopt.
  while((opt = getopt_long(argc, argv, "ac:l:d:", long_options, &option_index)) != -1) {
    switch(opt) {
    case 'c': {
      // Read configuration file.
      std::ifstream conf_file(optarg);
      if (!conf_file.is_open()) {
	std::cerr << "Can't open configure-file." << std::endl;
	return EXIT_FAILURE;
      }

      // Read as JSON formated file.
      picojson::value v;
      std::string err = picojson::parse(v, conf_file);
      conf_file.close();
      if (!err.empty()) {
	std::cerr << err << std::endl;
	goto on_error;
      }
      conf = v.get<picojson::object>();
    } break;

    case 'l': {
      // Make 'apps' directive if don't exist yet.
      if (conf.find("apps") == conf.end()) {
	conf.insert(std::make_pair("apps", picojson::value(picojson::array())));
      }
      // Make per application directive in 'apps' directive.
      picojson::object app;
      app.insert(std::make_pair("type", picojson::value(std::string("llvm"))));
      app.insert(std::make_pair("file", picojson::value(std::string(optarg))));
      app.insert(std::make_pair("args", picojson::value(picojson::array())));

      picojson::array& apps = conf.at("apps").get<picojson::array>();
      apps.push_back(picojson::value(app));
    } break;

    case 'd': {
      if (conf.find("apps") == conf.end()) goto on_error;
      picojson::array& apps = conf.at("apps").get<picojson::array>();

      for (auto& it : apps) {
	picojson::object& app = it.get<picojson::object>();
	if (app.find("device") == app.end()) {
	  app.insert(std::make_pair("device", picojson::value(std::string(optarg))));
	}
      }
    } break;

    case ':':
    case '?': {
      printf("Unknown or required argument option -%c\n", opt);
      goto on_error;
    } break;
    }
  }

  // Deal with after '--' options for last listed application.
  for (int i = optind; i < argc; i ++) {
    if (conf.find("apps") == conf.end()) goto on_error;
    picojson::array& apps = conf.at("apps").get<picojson::array>();
    if (apps.size() == 0) goto on_error;
    // Get 'arg' directive and push options.
    picojson::object& app = apps.back().get<picojson::object>();
    if (app.find("args") == app.end()) {
      app.insert(std::make_pair("args", picojson::value(picojson::array())));
    }
    picojson::array& args = app.at("args").get<picojson::array>();
    args.push_back(picojson::value(std::string(argv[i])));
  }

  { // Run.
    NativeVm native_vm(conf);
    native_vm.run();

    // Finish
    return EXIT_SUCCESS;
  }

 on_error:
  printf("Usage : COMMAND -c path [-l path [-d device] [-- ...]]\n");
  return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>/**
 *  machina
 *
 *  Copyright (c) 2014, drmats
 *  All rights reserved.
 *
 *  https://github.com/drmats/machina
 */

#ifndef __MAIN_LOOP_CPP_
#define __MAIN_LOOP_CPP_ 1

#include "main_loop.hpp"
#include "machina.hpp"
#include "primitives.hpp"

namespace machina {

/**
 *  Default mouse-motion event handler (do-nothing).
 */
void MainLoop::default_handler_t::empty_mouse_motion (
    MainLoop *ml, const SDL_Event &e
) {
    return;
}

/**
 *  Mouse-motion event handler (look-around).
 */
void MainLoop::default_handler_t::look_around_camera (
    MainLoop *ml, const SDL_Event &e, const GLfloat yaw_direction
) {
    static const auto positive_fmod =
        [] (GLfloat val, GLfloat denom) -> GLfloat {
            return std::fmod(std::fmod(val, denom) + denom, denom);
        };
    ml->camera.pitch = positive_fmod(
        ml->camera.pitch + e.motion.yrel * 0.3f, 360.0f
    );
    ml->camera.yaw = positive_fmod(
        ml->camera.yaw + yaw_direction * e.motion.xrel * 0.3f, 360.0f
    );
    ml->camera.recompute_rotation();
}

/**
 *  Mouse-motion event handler (move-around).
 */
void MainLoop::default_handler_t::move_around_camera (
    MainLoop *ml, const SDL_Event &e
) {
    ml->camera.relative_translate(
        ml->camera.strafe, 1.0,
        e.motion.xrel * ml->camera.dist * 0.00215f
    );
    ml->camera.relative_translate(
        ml->camera.up, -1.0,
        e.motion.yrel * ml->camera.dist * 0.00215f
    );
}

/**
 *  Mouse-wheel default event handler (distance-to-target).
 */
void MainLoop::default_handler_t::mouse_wheel (
    MainLoop *ml, const SDL_Event &e
) {
    const Uint8 *keystate = SDL_GetKeyboardState(NULL);

    if (keystate[SDL_SCANCODE_LCTRL] == 1) {
        // set fovy
        ml->camera.projection.set_all([ml, e] () {
            auto all = ml->camera.projection.get_all();
            all[0] -= e.wheel.y*4;
            return all;
        }());
    } else {
        // set camera distance-to-target
        ml->camera.dist =
            10.0f + std::exp2(std::fabs(
                std::log2(ml->camera.dist - 10.0f) - e.wheel.y*0.2f
            ));
        ml->camera.recompute_rotation();
    }
}

/**
 *  Default mouse-buttons event handler.
 */
void MainLoop::default_handler_t::mouse_buttons (
    MainLoop *ml, const SDL_Event &e
) {
    switch (e.button.button) {

        // look-around camera
        case SDL_BUTTON_MIDDLE:
        case SDL_BUTTON_RIGHT:
            if (e.type == SDL_MOUSEBUTTONDOWN) {
                if (
                    ml->camera.pitch > 90.0f  &&
                    ml->camera.pitch <= 270.0f
                ) {
                    ml->handle.mouse_motion =
                        [ml] (const SDL_Event &e) -> void {
                            return ml->default_handler.look_around_camera(
                                ml, e, -1.0f
                            );
                        };
                } else {
                    ml->handle.mouse_motion =
                        [ml] (const SDL_Event &e) -> void {
                            return ml->default_handler.look_around_camera(
                                ml, e, 1.0f
                            );
                        };
                }
            } else if (e.type == SDL_MOUSEBUTTONUP) {
                ml->handle.mouse_motion =
                    [ml] (const SDL_Event &e) -> void {
                        return ml->default_handler.empty_mouse_motion(ml, e);
                    };
            }
            break;

        // move-around camera
        case SDL_BUTTON_LEFT:
            if (e.type == SDL_MOUSEBUTTONDOWN) {
                ml->handle.mouse_motion =
                    [ml] (const SDL_Event &e) -> void {
                        return ml->default_handler.move_around_camera(ml, e);
                    };
            } else if (e.type == SDL_MOUSEBUTTONUP) {
                ml->handle.mouse_motion =
                    [ml] (const SDL_Event &e) -> void {
                        return ml->default_handler.empty_mouse_motion(ml, e);
                    };
            }
            break;

        default:
            break;
    }
}

/**
 *  Default keyboard event handler.
 */
void MainLoop::default_handler_t::keyboard (
    MainLoop *ml, const SDL_Event &e
) {
    const Uint8 *keystate = SDL_GetKeyboardState(NULL);

    if (keystate[SDL_SCANCODE_ESCAPE] == 1) {
        ml->terminate();
        return;
    }
    switch (e.key.keysym.sym) {

        case SDLK_q:
            if (e.key.state == SDL_PRESSED) {

                ml->terminate();
            }
            break;

        case SDLK_c:
            if (e.key.state == SDL_PRESSED) {
                // reset position
                ml->camera.translation.reset();
                // reset camera.projection.fovy
                ml->camera.projection.set_all([ml] () {
                    auto all = ml->camera.projection.get_all();
                    all[0] = 60.0f;
                    return all;
                }());
            }

        default:
            break;
    }
}

/**
 *  Default window event handler.
 */
void MainLoop::default_handler_t::window (
    MainLoop *ml, const SDL_Event &e
) {
    switch (e.window.event) {
        case SDL_WINDOWEVENT_SIZE_CHANGED:
            ml->root->viewport.width = e.window.data1;
            ml->root->viewport.height = e.window.data2;
            ml->adjust_camera_aspect();
            break;
        case SDL_WINDOWEVENT_RESIZED:
            std::cout
                << "Resized surface: "
                << e.window.data1 << "x"
                << e.window.data2
                << std::endl;
            break;
        default:
            break;
    }
}

/**
 *  Initialize vital components.
 */
MainLoop::MainLoop (Machina *root):
    root{root}
{
    this->assign_default_handlers();
    this->setup_opengl();
}

/**
 *  Assign default handlers (mouse/keyboard).
 */
void MainLoop::assign_default_handlers () {
    this->handle.mouse_motion =
        [this] (const SDL_Event &e) -> void {
            return this->default_handler.empty_mouse_motion(this, e);
        };
    this->handle.mouse_wheel =
        [this] (const SDL_Event &e) -> void {
            return this->default_handler.mouse_wheel(this, e);
        };
    this->handle.mouse_buttons =
        [this] (const SDL_Event &e) -> void {
            return this->default_handler.mouse_buttons(this, e);
        };
    this->handle.keyboard =
        [this] (const SDL_Event &e) -> void {
            return this->default_handler.keyboard(this, e);
        };
    this->handle.window =
        [this] (const SDL_Event &e) -> void {
            return this->default_handler.window(this, e);
        };
}

/**
 *  Adjust camera aspect ratio.
 */
inline void MainLoop::adjust_camera_aspect () {
    glViewport(
        0, 0,
        this->root->viewport.width, this->root->viewport.height
    );
    this->camera.projection.set_all([this] () {
        auto all = this->camera.projection.get_all();
        all[1] =
            static_cast<GLfloat>(this->root->viewport.width) /
            static_cast<GLfloat>(this->root->viewport.height);
        return all;
    }());
}

/**
 *  Setup initial OpenGL parameters.
 */
void MainLoop::setup_opengl () {
    this->adjust_camera_aspect();

    glClearColor(0.05f, 0.05f, 0.15f, 0.0f);
    glPolygonMode(GL_FRONT, GL_FILL);
    glShadeModel(GL_SMOOTH);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    glFrontFace(GL_CCW);

    glEnable(GL_POINT_SMOOTH);
    glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);

    glEnable(GL_LINE_SMOOTH);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

/**
 *  Event-processing.
 */
inline void MainLoop::process_events () {
    while (this->running  &&  SDL_PollEvent(&this->event)) {
        try {
            switch (this->event.type) {
                case SDL_MOUSEMOTION:
                    this->handle.mouse_motion(this->event);
                    break;

                case SDL_MOUSEWHEEL:
                    this->handle.mouse_wheel(this->event);
                    break;

                case SDL_MOUSEBUTTONDOWN:
                case SDL_MOUSEBUTTONUP:
                    this->handle.mouse_buttons(this->event);
                    break;

                case SDL_KEYDOWN:
                case SDL_KEYUP:
                    this->handle.keyboard(this->event);
                    break;

                case SDL_WINDOWEVENT:
                    this->handle.window(this->event);
                    break;

                case SDL_QUIT:
                    this->terminate();
                    break;
            }
        } catch (std::bad_function_call &e) {
            std::cout
                << "MainLoop::process_events: "
                << e.what()
                << std::endl;
        }
    }
}

/**
 *  Drawing.
 */
inline void MainLoop::draw () const {
    glClear(
        GL_COLOR_BUFFER_BIT |
        GL_DEPTH_BUFFER_BIT
    );
    this->camera.establish_projection();
    this->camera.establish_modelview();

    primitives::axes_grid(160.0f, 10.0f);

    primitives::point_cube(160.0f*64.0f, 640.0f, 0.6f);
    primitives::this_thing(160.0f*2.0f, 10.0f, 1.0f, 1.0f, 1.0f, GL_POINTS);
    primitives::this_thing(160.0f*16.0f, 80.0f, 0.7f, 1.0f, 0.1f, GL_LINES);
}

/**
 *  Main application loop.
 */
void MainLoop::run () {
    this->running = true;
    while (this->running) {
        this->process_events();
        this->draw();
        SDL_GL_SwapWindow(this->root->main_window);
    }
}

/**
 *  Gracefully finish.
 */
void MainLoop::terminate () {
    this->running = false;
}

} // namespace machina

#endif
<commit_msg>MouseLeft + LCTRL + Mouse-y-motion = camera.out translate.<commit_after>/**
 *  machina
 *
 *  Copyright (c) 2014, drmats
 *  All rights reserved.
 *
 *  https://github.com/drmats/machina
 */

#ifndef __MAIN_LOOP_CPP_
#define __MAIN_LOOP_CPP_ 1

#include "main_loop.hpp"
#include "machina.hpp"
#include "primitives.hpp"

namespace machina {

/**
 *  Default mouse-motion event handler (do-nothing).
 */
void MainLoop::default_handler_t::empty_mouse_motion (
    MainLoop *ml, const SDL_Event &e
) {
    return;
}

/**
 *  Mouse-motion event handler (look-around).
 */
void MainLoop::default_handler_t::look_around_camera (
    MainLoop *ml, const SDL_Event &e, const GLfloat yaw_direction
) {
    static const auto positive_fmod =
        [] (GLfloat val, GLfloat denom) -> GLfloat {
            return std::fmod(std::fmod(val, denom) + denom, denom);
        };
    ml->camera.pitch = positive_fmod(
        ml->camera.pitch + e.motion.yrel * 0.3f, 360.0f
    );
    ml->camera.yaw = positive_fmod(
        ml->camera.yaw + yaw_direction * e.motion.xrel * 0.3f, 360.0f
    );
    ml->camera.recompute_rotation();
}

/**
 *  Mouse-motion event handler (move-around).
 */
void MainLoop::default_handler_t::move_around_camera (
    MainLoop *ml, const SDL_Event &e
) {
    const Uint8 *keystate = SDL_GetKeyboardState(NULL);

    ml->camera.relative_translate(
        ml->camera.strafe, 1.0,
        e.motion.xrel * ml->camera.dist * 0.00215f
    );
    if (keystate[SDL_SCANCODE_LCTRL] == 1) {
        ml->camera.relative_translate(
            ml->camera.out, 1.0,
            e.motion.yrel * ml->camera.dist * 0.00215f
        );
    } else {
        ml->camera.relative_translate(
            ml->camera.up, -1.0,
            e.motion.yrel * ml->camera.dist * 0.00215f
        );
    }
}

/**
 *  Mouse-wheel default event handler (distance-to-target).
 */
void MainLoop::default_handler_t::mouse_wheel (
    MainLoop *ml, const SDL_Event &e
) {
    const Uint8 *keystate = SDL_GetKeyboardState(NULL);

    if (keystate[SDL_SCANCODE_LCTRL] == 1) {
        // set fovy
        ml->camera.projection.set_all([ml, e] () {
            auto all = ml->camera.projection.get_all();
            all[0] -= e.wheel.y*4;
            return all;
        }());
    } else {
        // set camera distance-to-target
        ml->camera.dist =
            10.0f + std::exp2(std::fabs(
                std::log2(ml->camera.dist - 10.0f) - e.wheel.y*0.2f
            ));
        ml->camera.recompute_rotation();
    }
}

/**
 *  Default mouse-buttons event handler.
 */
void MainLoop::default_handler_t::mouse_buttons (
    MainLoop *ml, const SDL_Event &e
) {
    switch (e.button.button) {

        // look-around camera
        case SDL_BUTTON_MIDDLE:
        case SDL_BUTTON_RIGHT:
            if (e.type == SDL_MOUSEBUTTONDOWN) {
                if (
                    ml->camera.pitch > 90.0f  &&
                    ml->camera.pitch <= 270.0f
                ) {
                    ml->handle.mouse_motion =
                        [ml] (const SDL_Event &e) -> void {
                            return ml->default_handler.look_around_camera(
                                ml, e, -1.0f
                            );
                        };
                } else {
                    ml->handle.mouse_motion =
                        [ml] (const SDL_Event &e) -> void {
                            return ml->default_handler.look_around_camera(
                                ml, e, 1.0f
                            );
                        };
                }
            } else if (e.type == SDL_MOUSEBUTTONUP) {
                ml->handle.mouse_motion =
                    [ml] (const SDL_Event &e) -> void {
                        return ml->default_handler.empty_mouse_motion(ml, e);
                    };
            }
            break;

        // move-around camera
        case SDL_BUTTON_LEFT:
            if (e.type == SDL_MOUSEBUTTONDOWN) {
                ml->handle.mouse_motion =
                    [ml] (const SDL_Event &e) -> void {
                        return ml->default_handler.move_around_camera(ml, e);
                    };
            } else if (e.type == SDL_MOUSEBUTTONUP) {
                ml->handle.mouse_motion =
                    [ml] (const SDL_Event &e) -> void {
                        return ml->default_handler.empty_mouse_motion(ml, e);
                    };
            }
            break;

        default:
            break;
    }
}

/**
 *  Default keyboard event handler.
 */
void MainLoop::default_handler_t::keyboard (
    MainLoop *ml, const SDL_Event &e
) {
    const Uint8 *keystate = SDL_GetKeyboardState(NULL);

    if (keystate[SDL_SCANCODE_ESCAPE] == 1) {
        ml->terminate();
        return;
    }
    switch (e.key.keysym.sym) {

        case SDLK_q:
            if (e.key.state == SDL_PRESSED) {

                ml->terminate();
            }
            break;

        case SDLK_c:
            if (e.key.state == SDL_PRESSED) {
                // reset position
                ml->camera.translation.reset();
                // reset camera.projection.fovy
                ml->camera.projection.set_all([ml] () {
                    auto all = ml->camera.projection.get_all();
                    all[0] = 60.0f;
                    return all;
                }());
            }

        default:
            break;
    }
}

/**
 *  Default window event handler.
 */
void MainLoop::default_handler_t::window (
    MainLoop *ml, const SDL_Event &e
) {
    switch (e.window.event) {
        case SDL_WINDOWEVENT_SIZE_CHANGED:
            ml->root->viewport.width = e.window.data1;
            ml->root->viewport.height = e.window.data2;
            ml->adjust_camera_aspect();
            break;
        case SDL_WINDOWEVENT_RESIZED:
            std::cout
                << "Resized surface: "
                << e.window.data1 << "x"
                << e.window.data2
                << std::endl;
            break;
        default:
            break;
    }
}

/**
 *  Initialize vital components.
 */
MainLoop::MainLoop (Machina *root):
    root{root}
{
    this->assign_default_handlers();
    this->setup_opengl();
}

/**
 *  Assign default handlers (mouse/keyboard).
 */
void MainLoop::assign_default_handlers () {
    this->handle.mouse_motion =
        [this] (const SDL_Event &e) -> void {
            return this->default_handler.empty_mouse_motion(this, e);
        };
    this->handle.mouse_wheel =
        [this] (const SDL_Event &e) -> void {
            return this->default_handler.mouse_wheel(this, e);
        };
    this->handle.mouse_buttons =
        [this] (const SDL_Event &e) -> void {
            return this->default_handler.mouse_buttons(this, e);
        };
    this->handle.keyboard =
        [this] (const SDL_Event &e) -> void {
            return this->default_handler.keyboard(this, e);
        };
    this->handle.window =
        [this] (const SDL_Event &e) -> void {
            return this->default_handler.window(this, e);
        };
}

/**
 *  Adjust camera aspect ratio.
 */
inline void MainLoop::adjust_camera_aspect () {
    glViewport(
        0, 0,
        this->root->viewport.width, this->root->viewport.height
    );
    this->camera.projection.set_all([this] () {
        auto all = this->camera.projection.get_all();
        all[1] =
            static_cast<GLfloat>(this->root->viewport.width) /
            static_cast<GLfloat>(this->root->viewport.height);
        return all;
    }());
}

/**
 *  Setup initial OpenGL parameters.
 */
void MainLoop::setup_opengl () {
    this->adjust_camera_aspect();

    glClearColor(0.05f, 0.05f, 0.15f, 0.0f);
    glPolygonMode(GL_FRONT, GL_FILL);
    glShadeModel(GL_SMOOTH);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    glFrontFace(GL_CCW);

    glEnable(GL_POINT_SMOOTH);
    glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);

    glEnable(GL_LINE_SMOOTH);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

/**
 *  Event-processing.
 */
inline void MainLoop::process_events () {
    while (this->running  &&  SDL_PollEvent(&this->event)) {
        try {
            switch (this->event.type) {
                case SDL_MOUSEMOTION:
                    this->handle.mouse_motion(this->event);
                    break;

                case SDL_MOUSEWHEEL:
                    this->handle.mouse_wheel(this->event);
                    break;

                case SDL_MOUSEBUTTONDOWN:
                case SDL_MOUSEBUTTONUP:
                    this->handle.mouse_buttons(this->event);
                    break;

                case SDL_KEYDOWN:
                case SDL_KEYUP:
                    this->handle.keyboard(this->event);
                    break;

                case SDL_WINDOWEVENT:
                    this->handle.window(this->event);
                    break;

                case SDL_QUIT:
                    this->terminate();
                    break;
            }
        } catch (std::bad_function_call &e) {
            std::cout
                << "MainLoop::process_events: "
                << e.what()
                << std::endl;
        }
    }
}

/**
 *  Drawing.
 */
inline void MainLoop::draw () const {
    glClear(
        GL_COLOR_BUFFER_BIT |
        GL_DEPTH_BUFFER_BIT
    );
    this->camera.establish_projection();
    this->camera.establish_modelview();

    primitives::axes_grid(160.0f, 10.0f);

    primitives::point_cube(160.0f*64.0f, 640.0f, 0.6f);
    primitives::this_thing(160.0f*2.0f, 10.0f, 1.0f, 1.0f, 1.0f, GL_POINTS);
    primitives::this_thing(160.0f*16.0f, 80.0f, 0.7f, 1.0f, 0.1f, GL_LINES);
}

/**
 *  Main application loop.
 */
void MainLoop::run () {
    this->running = true;
    while (this->running) {
        this->process_events();
        this->draw();
        SDL_GL_SwapWindow(this->root->main_window);
    }
}

/**
 *  Gracefully finish.
 */
void MainLoop::terminate () {
    this->running = false;
}

} // namespace machina

#endif
<|endoftext|>


<commit_before>#include <stdlib.h>

#include <mruby.h>
#include <mruby/variable.h>
#include <mruby/data.h>
#include <mruby/string.h>

#include <Fl/Fl_Widget.h>

#include "macros.h"
#include "helpers.h"
#include "widget.h"
#include "group.h"

// FLTK::Widget#align
// FLTK::Widget#align=(value)
IMPLEMENT_FIXNUM_ATTRIBUTE_ACCESSOR( widget, align, Fl_Widget, align );

// For FLTK::Widget#callback
typedef struct {
  mrb_state *mrb;
  mrb_value self;
} mrb_fltk_widget_callback_context;

// For FLTK::Widget#callback
void mrb_fltk_widget_callback_function( Fl_Widget *fl_widget, void *data ) {
  mrb_fltk_widget_callback_context *context = (mrb_fltk_widget_callback_context *)data;
  mrb_state *mrb = context->mrb;
  mrb_value self = context->self;

  mrb_value block = mrb_iv_get( mrb, self, mrb_intern_cstr( mrb, "callback" ) );

  mrb_funcall_with_block( mrb, self, mrb_intern_cstr( mrb, "instance_eval" ), 0, NULL, block );
}

// FLTK::Widget#callback
// Gets or sets the current callback function for the widget.
mrb_value mrb_fltk_widget_callback_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  mrb_value block = mrb_nil_value();

  mrb_get_args( mrb, "&", &block );

  if( !mrb_nil_p( block ) ) {
    // TODO: Free the existing context struct if it already exists?
    mrb_iv_set( mrb, self, mrb_intern_cstr( mrb, "callback" ), block );

    mrb_fltk_widget_callback_context *context = (mrb_fltk_widget_callback_context *)malloc( sizeof( mrb_fltk_widget_callback_context ) );
    context->mrb = mrb;
    context->self = self;

    fl_widget->callback( mrb_fltk_widget_callback_function, context );
  } else {
    block = mrb_iv_get( mrb, self, mrb_intern_cstr( mrb, "callback" ) );
  }

  return block;
}

// FLTK::Widget#height
IMPLEMENT_FIXNUM_ATTRIBUTE_READER( widget, height, Fl_Widget, h );

// FLTK::Widget#image
// Gets the image that is used as part of the widget label.
// mrb_value
// mrb_fltk_widget_image_get_method( mrb_state *mrb, mrb_value self ) {
//   CONTEXT_SETUP( widget );
//
//   struct RClass *mrb_fltk_class, *mrb_fltk_image_class;
//
//   mrb_fltk_class = mrb_class_get( mrb, "FLTK" );
//   mrb_fltk_image_class = mrb_class_ptr( mrb_const_get( mrb, mrb_obj_value( mrb_fltk_class ), mrb_intern_cstr( mrb, "Image" ) ) );
//
//   mrb_value args[1];
//   args[0] = mrb_obj_value(
//     Data_Wrap_Struct( mrb, mrb->object_class, &fltk_image_type, (void *)context->fl_instance->image() ) );
//
//   return mrb_class_new_instance( mrb, 1, args, mrb_fltk_image_class );
// }

// FLTK::Widget#image=(image)
// Sets the image to use as part of the widget label. Must be a FLTK::Image
// mrb_value
// mrb_fltk_widget_image_set_method( mrb_state *mrb, mrb_value self ) { // TODO: Must be a FLTK::Image
//   CONTEXT_SETUP( widget );
//
//   mrb_value image;
//   mrb_get_args( mrb, "o", &image );
//
//   if( !mrb_nil_p( image ) ) {
//     mrb_value image_value_context;
//     mrb_fltk_image_context *image_context;
//
//     image_value_context = mrb_iv_get( mrb, image, mrb_intern_cstr( mrb, "context" ) );
//     Data_Get_Struct( mrb, image_value_context, &fltk_image_type, image_context );
//
//     context->fl_instance->image( (Fl_Image *)image_context->fl_instance );
//   } else {
//     context->fl_instance->image( NULL ); // TODO Raise Error!
//   }
//
//   return mrb_nil_value();
// }

// FLTK::Widget#hide
// Makes a widget invisible.
mrb_value mrb_fltk_widget_hide_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  fl_widget->hide();

  return self;
}

// FLTK::Widget#label
// FLTK::Widget#label=(value)
IMPLEMENT_STRING_ATTRIBUTE_ACCESSOR( widget, label, Fl_Widget, label );

// FLTK::Widget#label_font
// FLTK::Widget#label_font=(value)
IMPLEMENT_FIXNUM_ATTRIBUTE_ACCESSOR( widget, label_font, Fl_Widget, labelfont );

// FLTK::Widget#label_size
// FLTK::Widget#label_size=(value)
IMPLEMENT_FIXNUM_ATTRIBUTE_ACCESSOR( widget, label_size, Fl_Widget, labelsize );

// FLTK::Widget#parent
// Returns the parent widget
mrb_value mrb_fltk_widget_parent_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self ); // TODO: Needed?

  return mrb_iv_get( mrb, self, mrb_intern_cstr( mrb, "parent" ) );
}

// FLTK::Widget#redraw
mrb_value mrb_fltk_widget_redraw_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  fl_widget->redraw();

  return self;
}

// FLTK::Widget#show
// Makes a widget visible.
mrb_value mrb_fltk_widget_show_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  fl_widget->show();

  return self;
}

// FLTK::Widget#take_focus
// Gives the widget the keyboard focus.
mrb_value mrb_fltk_widget_take_focus_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  fl_widget->take_focus();

  return self;
}

// FLTK::Widget#visible?
// Returns whether a widget is visible.
mrb_value mrb_fltk_widget_visible_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  return fl_widget->visible() ? mrb_true_value() : mrb_false_value();
}

// FLTK::Widget#when
// Returns the conditions under which the callback is called.
// FLTK::Widget#when=(value)
// Sets the flags used to decide when a callback is called.
IMPLEMENT_FIXNUM_ATTRIBUTE_ACCESSOR( widget, when, Fl_Widget, when );

// FLTK::Widget#width
IMPLEMENT_FIXNUM_ATTRIBUTE_READER( widget, width, Fl_Widget, w );

// FLTK::Widget#x
IMPLEMENT_FIXNUM_ATTRIBUTE_READER( widget, x, Fl_Widget, x );

// FLTK::Widget#y
IMPLEMENT_FIXNUM_ATTRIBUTE_READER( widget, y, Fl_Widget, y );

void mrb_fltk_widget_class_init( mrb_state *mrb ) {
  ARENA_SAVE;

  struct RClass *mrb_fltk_module = mrb_class_get( mrb, "FLTK" );

  DEFINE_CLASS( widget, Widget, mrb->object_class );

  DEFINE_INSTANCE_METHOD_ACCESSOR( widget, align );
  // DEFINE_FIXNUM_ATTRIBUTE_ACCESSOR( widget, box, Fl_Widget, box );
  DEFINE_INSTANCE_METHOD( widget, callback, ARGS_OPT( 1 ) );
  DEFINE_INSTANCE_METHOD_GETTER( widget, height );
  DEFINE_INSTANCE_METHOD( widget, hide, ARGS_NONE() );
  DEFINE_INSTANCE_METHOD_ACCESSOR( widget, label );
  DEFINE_INSTANCE_METHOD_ACCESSOR( widget, label_font );
  DEFINE_INSTANCE_METHOD_ACCESSOR( widget, label_size );
  // DEFINE_FIXNUM_ATTRIBUTE_ACCESSOR( widget, label_size, Fl_Widget, labelsize );
  DEFINE_INSTANCE_METHOD( widget, parent, ARGS_NONE() );
  DEFINE_INSTANCE_METHOD( widget, redraw, ARGS_NONE() );
  DEFINE_INSTANCE_METHOD( widget, show, ARGS_NONE() );
  DEFINE_INSTANCE_METHOD( widget, take_focus, ARGS_NONE() );
  mrb_define_method( mrb, mrb_fltk_widget, "visible?", mrb_fltk_widget_visible_instance_method, ARGS_NONE() ); // TODO: DEFINE_INSTANCE_QUERY_METHOD macro
  DEFINE_INSTANCE_METHOD_ACCESSOR( widget, when );
  DEFINE_INSTANCE_METHOD_GETTER( widget, width );
  DEFINE_INSTANCE_METHOD_GETTER( widget, x );
  DEFINE_INSTANCE_METHOD_GETTER( widget, y );

  // mrb_define_method( mrb, mrb_fltk_widget_class, "image", mrb_fltk_widget_image_getter_instance_method, ARGS_NONE() );
  // mrb_define_method( mrb, mrb_fltk_widget_class, "image=", mrb_fltk_widget_image_setter_instance_method, ARGS_REQ( 1 ) );

  ARENA_RESTORE;
}
<commit_msg>FLTK::Widget #activate and #deactivate<commit_after>#include <stdlib.h>

#include <mruby.h>
#include <mruby/variable.h>
#include <mruby/data.h>
#include <mruby/string.h>

#include <Fl/Fl_Widget.h>

#include "macros.h"
#include "helpers.h"
#include "widget.h"
#include "group.h"

// FLTK::Widget#activate
// Activate the widget.
mrb_value mrb_fltk_widget_activate_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  fl_widget->activate();

  return self;
}

// FLTK::Widget#align
// FLTK::Widget#align=(value)
IMPLEMENT_FIXNUM_ATTRIBUTE_ACCESSOR( widget, align, Fl_Widget, align );

// For FLTK::Widget#callback
typedef struct {
  mrb_state *mrb;
  mrb_value self;
} mrb_fltk_widget_callback_context;

// For FLTK::Widget#callback
void mrb_fltk_widget_callback_function( Fl_Widget *fl_widget, void *data ) {
  mrb_fltk_widget_callback_context *context = (mrb_fltk_widget_callback_context *)data;
  mrb_state *mrb = context->mrb;
  mrb_value self = context->self;

  mrb_value block = mrb_iv_get( mrb, self, mrb_intern_cstr( mrb, "callback" ) );

  mrb_funcall_with_block( mrb, self, mrb_intern_cstr( mrb, "instance_eval" ), 0, NULL, block );
}

// FLTK::Widget#callback
// Gets or sets the current callback function for the widget.
mrb_value mrb_fltk_widget_callback_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  mrb_value block = mrb_nil_value();

  mrb_get_args( mrb, "&", &block );

  if( !mrb_nil_p( block ) ) {
    // TODO: Free the existing context struct if it already exists?
    mrb_iv_set( mrb, self, mrb_intern_cstr( mrb, "callback" ), block );

    mrb_fltk_widget_callback_context *context = (mrb_fltk_widget_callback_context *)malloc( sizeof( mrb_fltk_widget_callback_context ) );
    context->mrb = mrb;
    context->self = self;

    fl_widget->callback( mrb_fltk_widget_callback_function, context );
  } else {
    block = mrb_iv_get( mrb, self, mrb_intern_cstr( mrb, "callback" ) );
  }

  return block;
}

// FLTK::Widget#deactivate
// Activate the widget.
mrb_value mrb_fltk_widget_deactivate_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  fl_widget->deactivate();

  return self;
}

// FLTK::Widget#height
IMPLEMENT_FIXNUM_ATTRIBUTE_READER( widget, height, Fl_Widget, h );

// FLTK::Widget#image
// Gets the image that is used as part of the widget label.
// mrb_value
// mrb_fltk_widget_image_get_method( mrb_state *mrb, mrb_value self ) {
//   CONTEXT_SETUP( widget );
//
//   struct RClass *mrb_fltk_class, *mrb_fltk_image_class;
//
//   mrb_fltk_class = mrb_class_get( mrb, "FLTK" );
//   mrb_fltk_image_class = mrb_class_ptr( mrb_const_get( mrb, mrb_obj_value( mrb_fltk_class ), mrb_intern_cstr( mrb, "Image" ) ) );
//
//   mrb_value args[1];
//   args[0] = mrb_obj_value(
//     Data_Wrap_Struct( mrb, mrb->object_class, &fltk_image_type, (void *)context->fl_instance->image() ) );
//
//   return mrb_class_new_instance( mrb, 1, args, mrb_fltk_image_class );
// }

// FLTK::Widget#image=(image)
// Sets the image to use as part of the widget label. Must be a FLTK::Image
// mrb_value
// mrb_fltk_widget_image_set_method( mrb_state *mrb, mrb_value self ) { // TODO: Must be a FLTK::Image
//   CONTEXT_SETUP( widget );
//
//   mrb_value image;
//   mrb_get_args( mrb, "o", &image );
//
//   if( !mrb_nil_p( image ) ) {
//     mrb_value image_value_context;
//     mrb_fltk_image_context *image_context;
//
//     image_value_context = mrb_iv_get( mrb, image, mrb_intern_cstr( mrb, "context" ) );
//     Data_Get_Struct( mrb, image_value_context, &fltk_image_type, image_context );
//
//     context->fl_instance->image( (Fl_Image *)image_context->fl_instance );
//   } else {
//     context->fl_instance->image( NULL ); // TODO Raise Error!
//   }
//
//   return mrb_nil_value();
// }

// FLTK::Widget#hide
// Makes a widget invisible.
mrb_value mrb_fltk_widget_hide_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  fl_widget->hide();

  return self;
}

// FLTK::Widget#label
// FLTK::Widget#label=(value)
IMPLEMENT_STRING_ATTRIBUTE_ACCESSOR( widget, label, Fl_Widget, label );

// FLTK::Widget#label_font
// FLTK::Widget#label_font=(value)
IMPLEMENT_FIXNUM_ATTRIBUTE_ACCESSOR( widget, label_font, Fl_Widget, labelfont );

// FLTK::Widget#label_size
// FLTK::Widget#label_size=(value)
IMPLEMENT_FIXNUM_ATTRIBUTE_ACCESSOR( widget, label_size, Fl_Widget, labelsize );

// FLTK::Widget#parent
// Returns the parent widget
mrb_value mrb_fltk_widget_parent_instance_method( mrb_state *mrb, mrb_value self ) {
  return mrb_iv_get( mrb, self, mrb_intern_cstr( mrb, "parent" ) );
}

// FLTK::Widget#redraw
mrb_value mrb_fltk_widget_redraw_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  fl_widget->redraw();

  return self;
}

// FLTK::Widget#show
// Makes a widget visible.
mrb_value mrb_fltk_widget_show_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  fl_widget->show();

  return self;
}

// FLTK::Widget#take_focus
// Gives the widget the keyboard focus.
mrb_value mrb_fltk_widget_take_focus_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  fl_widget->take_focus();

  return self;
}

// FLTK::Widget#visible?
// Returns whether a widget is visible.
mrb_value mrb_fltk_widget_visible_instance_method( mrb_state *mrb, mrb_value self ) {
  GET_DATA( fl_widget, Fl_Widget, self );

  return fl_widget->visible() ? mrb_true_value() : mrb_false_value();
}

// FLTK::Widget#when
// Returns the conditions under which the callback is called.
// FLTK::Widget#when=(value)
// Sets the flags used to decide when a callback is called.
IMPLEMENT_FIXNUM_ATTRIBUTE_ACCESSOR( widget, when, Fl_Widget, when );

// FLTK::Widget#width
IMPLEMENT_FIXNUM_ATTRIBUTE_READER( widget, width, Fl_Widget, w );

// FLTK::Widget#x
IMPLEMENT_FIXNUM_ATTRIBUTE_READER( widget, x, Fl_Widget, x );

// FLTK::Widget#y
IMPLEMENT_FIXNUM_ATTRIBUTE_READER( widget, y, Fl_Widget, y );

void mrb_fltk_widget_class_init( mrb_state *mrb ) {
  ARENA_SAVE;

  struct RClass *mrb_fltk_module = mrb_class_get( mrb, "FLTK" );

  DEFINE_CLASS( widget, Widget, mrb->object_class );

  DEFINE_INSTANCE_METHOD( widget, activate, ARGS_NONE() );
  DEFINE_INSTANCE_METHOD_ACCESSOR( widget, align );
  // DEFINE_FIXNUM_ATTRIBUTE_ACCESSOR( widget, box, Fl_Widget, box );
  DEFINE_INSTANCE_METHOD( widget, callback, ARGS_OPT( 1 ) );
  DEFINE_INSTANCE_METHOD( widget, deactivate, ARGS_NONE() );
  DEFINE_INSTANCE_METHOD_GETTER( widget, height );
  DEFINE_INSTANCE_METHOD( widget, hide, ARGS_NONE() );
  DEFINE_INSTANCE_METHOD_ACCESSOR( widget, label );
  DEFINE_INSTANCE_METHOD_ACCESSOR( widget, label_font );
  DEFINE_INSTANCE_METHOD_ACCESSOR( widget, label_size );
  // DEFINE_FIXNUM_ATTRIBUTE_ACCESSOR( widget, label_size, Fl_Widget, labelsize );
  DEFINE_INSTANCE_METHOD( widget, parent, ARGS_NONE() );
  DEFINE_INSTANCE_METHOD( widget, redraw, ARGS_NONE() );
  DEFINE_INSTANCE_METHOD( widget, show, ARGS_NONE() );
  DEFINE_INSTANCE_METHOD( widget, take_focus, ARGS_NONE() );
  mrb_define_method( mrb, mrb_fltk_widget, "visible?", mrb_fltk_widget_visible_instance_method, ARGS_NONE() ); // TODO: DEFINE_INSTANCE_QUERY_METHOD macro
  DEFINE_INSTANCE_METHOD_ACCESSOR( widget, when );
  DEFINE_INSTANCE_METHOD_GETTER( widget, width );
  DEFINE_INSTANCE_METHOD_GETTER( widget, x );
  DEFINE_INSTANCE_METHOD_GETTER( widget, y );

  // mrb_define_method( mrb, mrb_fltk_widget_class, "image", mrb_fltk_widget_image_getter_instance_method, ARGS_NONE() );
  // mrb_define_method( mrb, mrb_fltk_widget_class, "image=", mrb_fltk_widget_image_setter_instance_method, ARGS_REQ( 1 ) );

  ARENA_RESTORE;
}
<|endoftext|>