<commit_before>#include "circuitview.h"

CircuitView::CircuitView(QWidget *parent, Circuit *circuit) :
    QWidget(parent)
{
    active_ = false;
    circuit_ = NULL;
    touchDragging_ = false;
    pixelsPerUnit_ = 1;
    zoom_ = MAX_ZOOM / 6;

    setCircuit(circuit);
}

bool CircuitView::event(QEvent *event)
{
    if (event->type() == QEvent::TouchBegin ||
        event->type() == QEvent::TouchUpdate ||
        event->type() == QEvent::TouchEnd ||
        event->type() == QEvent::TouchCancel)
    {
        touchEvent((QTouchEvent*) event);
    }

    return this->QWidget::event(event);
}

void CircuitView::mouseMoveEvent(QMouseEvent *event)
{
    controller_->mouseMoveEvent(this, event);
}

void CircuitView::mousePressEvent(QMouseEvent *event)
{
    controller_->mousePressEvent(this, event);
}

void CircuitView::mouseReleaseEvent(QMouseEvent *event) // Execute before the last move event to get the velocity
{
    controller_->mouseReleaseEvent(this, event);
}

void CircuitView::wheelEvent(QWheelEvent *event)
{
    controller_->wheelEvent(this, event);
}

void CircuitView::touchEvent(QTouchEvent *event)
{
    controller_->touchEvent(this, event);
}

void CircuitView::resizeEvent(QResizeEvent *event)
{
    updatePixelsPerUnit();
}

void CircuitView::paintEvent(QPaintEvent *event)
{
    bool doRepaint = false;

    if (isPositionFalloffEnabled_) {
        if (sqrt(pow(positionVelocity().x(), 2) + pow(positionVelocity().y(), 2)) > 0.001) {
            translate(positionVelocity(), false);
            setPositionVelocity(positionVelocity() - positionVelocity() * MIN_ZOOM * 0.45 / zoom());
        }
    } else {
        translate(positionVelocity(), false);
        setPositionVelocity(0, 0);
    }

    QPainter painter;

    painter.begin(this);

    QFont font("Tahoma", 15);
    painter.setFont(font);

    drawGrid(event, painter);
    drawComponents(event, painter);

    painter.end();

    if (doRepaint)
        update();
}

void CircuitView::drawGrid(QPaintEvent *event, QPainter &painter)
{
    QPen pen = painter.pen();
    pen.setColor(QColor("#404040"));
    pen.setWidth(1);
    painter.setPen(pen);

    double x = Math::dmod(double(width()/2.0) - pixelsPerUnit()*position().x(), pixelsPerUnit());
    double y = Math::dmod(double(height()/2.0) - pixelsPerUnit()*position().y(), pixelsPerUnit());
    for (x; x < width(); x += pixelsPerUnit()) {
        painter.drawLine(round(x), 0, round(x), height());
    }

    for (y; y < height(); y += pixelsPerUnit()) {
        painter.drawLine(0, round(y), width(), round(y));
    }
}

void CircuitView::drawComponents(QPaintEvent *event, QPainter &painter)
{
    painter.setRenderHint(QPainter::Antialiasing);

    QTransform transform = painter.transform();

    foreach(CircuitComponent *component, circuit()->components()) {
        component->prepareDraw(painter, position(), size(), pixelsPerUnit_);
        component->draw(painter);
        painter.setTransform(transform);
    }
}

bool CircuitView::isActive() { return active_; }
void CircuitView::setActive(bool active) { active_ = active; }

Circuit* CircuitView::circuit() { return circuit_; }
void CircuitView::setCircuit(Circuit* circuit)
{
    if (circuit != NULL) {
        disconnect(circuit, SIGNAL(updated()), this, SLOT(repaint()));
    }
    circuit_ = circuit; update();
    connect(circuit, SIGNAL(updated()), this, SLOT(repaint()));
}

CircuitViewController* CircuitView::controller() { return controller_; }
void CircuitView::setController(CircuitViewController *controller)
{
    controller_ = controller;
}

QPointF CircuitView::mapFromCoordinate(QPointF point) { return mapFromCoordinate(point.x(), point.y()); }
QPointF CircuitView::mapFromCoordinate(double x, double y)
{
    QPointF coord;
    coord.setX((x - position().x()) * pixelsPerUnit() / width() + 0.5);
    coord.setY((y - position().y()) * pixelsPerUnit() / height() + 0.5);
    return coord;
}

QPointF CircuitView::mapToCoordinate(QPointF point) { return mapToCoordinate(point.x(), point.y()); }
QPointF CircuitView::mapToCoordinate(double x, double y)
{
    QPointF pos;
    pos.setX((x - 0.5)*width() / pixelsPerUnit() + position().x());
    pos.setY((y - 0.5)*height() / pixelsPerUnit() + position().y());
    return pos;
}

QPointF CircuitView::toScreen(QPoint point) { return toScreen(point.x(), point.y()); }
QPointF CircuitView::toScreen(QPointF point) { return toScreen(point.x(), point.y()); }
QPointF CircuitView::toScreen(double x, double y) {
    return QPointF(x/width(), y/height());
}

QPoint CircuitView::toPixels(QPointF point) { return toPixels(point.x(), point.y()); }
QPoint CircuitView::toPixels(double x, double y)
{
    return QPoint(x*width(), y*height());
}

bool CircuitView::isPositionFalloffEnabled() { return isPositionFalloffEnabled_; }
void CircuitView::setPositionFalloffEnabled(bool enabled) { isPositionFalloffEnabled_ = enabled; }

void CircuitView::translate(double x, double y, bool update) { translate(QVector2D(x, y), update); }
void CircuitView::translate(QVector2D position, bool update) { position_ += position.toPointF(); if(update) this->update(); }

double CircuitView::pixelsPerUnit() { return pixelsPerUnit_; }
void CircuitView::updatePixelsPerUnit() { pixelsPerUnit_ = qMax<double>(width(), height()) / MIN_ZOOM * zoom() / MAX_ZOOM; }

QPointF CircuitView::position() { return position_; }
void CircuitView::setPosition(QPointF position) { setPosition(position.x(), position.y()); }
void CircuitView::setPosition(double x, double y)
{
    position_.setX(x);
    position_.setY(y);
    update();
}

QVector2D CircuitView::positionVelocity() { return positionVelocity_; }
QVector2D CircuitView::lastPositionVelocity() { return lastPositionVelocity_; }
void CircuitView::setPositionVelocity(QVector2D velocity) { setPositionVelocity(velocity.x(), velocity.y()); }
void CircuitView::setPositionVelocity(double x, double y)
{
    lastPositionVelocity_.setX(positionVelocity_.x());
    lastPositionVelocity_.setY(positionVelocity_.y());
    positionVelocity_.setX(x);
    positionVelocity_.setY(y);
}

double CircuitView::zoom() { return zoom_; }
void CircuitView::setZoom(double zoom, bool update) { setZoom(zoom, mapToCoordinate(QPointF(0.5, 0.5)), update); }
void CircuitView::setZoom(double zoom, QPointF point, bool update)
{
    QPointF coord = mapToCoordinate(point);
    zoom_ = qMin<double>(MAX_ZOOM, qMax<double>(MIN_ZOOM, zoom));
    updatePixelsPerUnit();

    setPosition(coord - QPointF(width()*(point.x()-0.5)/pixelsPerUnit(), height()*(point.y()-0.5)/pixelsPerUnit()));

    if (update)
        repaint();

}
void CircuitView::setZoom(QPointF pointAi, QPointF pointBi, QPointF pointAf, QPointF pointBf, bool update)
{
    QPointF hotSpot = (pointAf + pointBf)/2;

    QVector2D a(width()*(pointAi.x() - pointBi.x()), height()*(pointAi.y() - pointBi.y()));
    QVector2D b(width()*(pointAf.x() - pointBf.x()), height()*(pointAf.y() - pointBf.y()));

    double newZoom = zoom() * b.length() / a.length();
    setZoom(newZoom, hotSpot, update);
}
<commit_msg>Migrated user controls from the CircuitView to a dedicated controller<commit_after>#include "circuitview.h"

CircuitView::CircuitView(QWidget *parent, Circuit *circuit) :
    QWidget(parent)
{
    active_ = false;
    circuit_ = NULL;
    touchDragging_ = false;
    pixelsPerUnit_ = 1;
    zoom_ = MAX_ZOOM / 6;

    setController(new CircuitViewController());
    setCircuit(circuit);
}

bool CircuitView::event(QEvent *event)
{
    if (event->type() == QEvent::TouchBegin ||
        event->type() == QEvent::TouchUpdate ||
        event->type() == QEvent::TouchEnd ||
        event->type() == QEvent::TouchCancel)
    {
        touchEvent((QTouchEvent*) event);
    }

    return this->QWidget::event(event);
}

void CircuitView::mouseMoveEvent(QMouseEvent *event)
{
    controller_->mouseMoveEvent(this, event);
}

void CircuitView::mousePressEvent(QMouseEvent *event)
{
    controller_->mousePressEvent(this, event);
}

void CircuitView::mouseReleaseEvent(QMouseEvent *event) // Execute before the last move event to get the velocity
{
    controller_->mouseReleaseEvent(this, event);
}

void CircuitView::wheelEvent(QWheelEvent *event)
{
    controller_->wheelEvent(this, event);
}

void CircuitView::touchEvent(QTouchEvent *event)
{
    controller_->touchEvent(this, event);
}

void CircuitView::resizeEvent(QResizeEvent *event)
{
    updatePixelsPerUnit();
}

void CircuitView::paintEvent(QPaintEvent *event)
{
    bool doRepaint = false;

    if (isPositionFalloffEnabled_) {
        if (sqrt(pow(positionVelocity().x(), 2) + pow(positionVelocity().y(), 2)) > 0.001) {
            translate(positionVelocity(), false);
            setPositionVelocity(positionVelocity() - positionVelocity() * MIN_ZOOM * 0.45 / zoom());
        }
    } else {
        translate(positionVelocity(), false);
        setPositionVelocity(0, 0);
    }

    QPainter painter;

    painter.begin(this);

    QFont font("Tahoma", 15);
    painter.setFont(font);

    drawGrid(event, painter);
    drawComponents(event, painter);

    painter.end();

    if (doRepaint)
        update();
}

void CircuitView::drawGrid(QPaintEvent *event, QPainter &painter)
{
    QPen pen = painter.pen();
    pen.setColor(QColor("#404040"));
    pen.setWidth(1);
    painter.setPen(pen);

    double x = Math::dmod(double(width()/2.0) - pixelsPerUnit()*position().x(), pixelsPerUnit());
    double y = Math::dmod(double(height()/2.0) - pixelsPerUnit()*position().y(), pixelsPerUnit());
    for (x; x < width(); x += pixelsPerUnit()) {
        painter.drawLine(round(x), 0, round(x), height());
    }

    for (y; y < height(); y += pixelsPerUnit()) {
        painter.drawLine(0, round(y), width(), round(y));
    }
}

void CircuitView::drawComponents(QPaintEvent *event, QPainter &painter)
{
    painter.setRenderHint(QPainter::Antialiasing);

    QTransform transform = painter.transform();

    foreach(CircuitComponent *component, circuit()->components()) {
        component->prepareDraw(painter, position(), size(), pixelsPerUnit_);
        component->draw(painter);
        painter.setTransform(transform);
    }
}

bool CircuitView::isActive() { return active_; }
void CircuitView::setActive(bool active) { active_ = active; }

Circuit* CircuitView::circuit() { return circuit_; }
void CircuitView::setCircuit(Circuit* circuit)
{
    if (circuit != NULL) {
        disconnect(circuit, SIGNAL(updated()), this, SLOT(repaint()));
    }
    circuit_ = circuit; update();
    connect(circuit, SIGNAL(updated()), this, SLOT(repaint()));
}

CircuitViewController* CircuitView::controller() { return controller_; }
void CircuitView::setController(CircuitViewController *controller)
{
    controller_ = controller;
}

QPointF CircuitView::mapFromCoordinate(QPointF point) { return mapFromCoordinate(point.x(), point.y()); }
QPointF CircuitView::mapFromCoordinate(double x, double y)
{
    QPointF coord;
    coord.setX((x - position().x()) * pixelsPerUnit() / width() + 0.5);
    coord.setY((y - position().y()) * pixelsPerUnit() / height() + 0.5);
    return coord;
}

QPointF CircuitView::mapToCoordinate(QPointF point) { return mapToCoordinate(point.x(), point.y()); }
QPointF CircuitView::mapToCoordinate(double x, double y)
{
    QPointF pos;
    pos.setX((x - 0.5)*width() / pixelsPerUnit() + position().x());
    pos.setY((y - 0.5)*height() / pixelsPerUnit() + position().y());
    return pos;
}

QPointF CircuitView::toScreen(QPoint point) { return toScreen(point.x(), point.y()); }
QPointF CircuitView::toScreen(QPointF point) { return toScreen(point.x(), point.y()); }
QPointF CircuitView::toScreen(double x, double y) {
    return QPointF(x/width(), y/height());
}

QPoint CircuitView::toPixels(QPointF point) { return toPixels(point.x(), point.y()); }
QPoint CircuitView::toPixels(double x, double y)
{
    return QPoint(x*width(), y*height());
}

bool CircuitView::isPositionFalloffEnabled() { return isPositionFalloffEnabled_; }
void CircuitView::setPositionFalloffEnabled(bool enabled) { isPositionFalloffEnabled_ = enabled; }

void CircuitView::translate(double x, double y, bool update) { translate(QVector2D(x, y), update); }
void CircuitView::translate(QVector2D position, bool update) { position_ += position.toPointF(); if(update) this->update(); }

double CircuitView::pixelsPerUnit() { return pixelsPerUnit_; }
void CircuitView::updatePixelsPerUnit() { pixelsPerUnit_ = qMax<double>(width(), height()) / MIN_ZOOM * zoom() / MAX_ZOOM; }

QPointF CircuitView::position() { return position_; }
void CircuitView::setPosition(QPointF position) { setPosition(position.x(), position.y()); }
void CircuitView::setPosition(double x, double y)
{
    position_.setX(x);
    position_.setY(y);
    update();
}

QVector2D CircuitView::positionVelocity() { return positionVelocity_; }
QVector2D CircuitView::lastPositionVelocity() { return lastPositionVelocity_; }
void CircuitView::setPositionVelocity(QVector2D velocity) { setPositionVelocity(velocity.x(), velocity.y()); }
void CircuitView::setPositionVelocity(double x, double y)
{
    lastPositionVelocity_.setX(positionVelocity_.x());
    lastPositionVelocity_.setY(positionVelocity_.y());
    positionVelocity_.setX(x);
    positionVelocity_.setY(y);
}

double CircuitView::zoom() { return zoom_; }
void CircuitView::setZoom(double zoom, bool update) { setZoom(zoom, mapToCoordinate(QPointF(0.5, 0.5)), update); }
void CircuitView::setZoom(double zoom, QPointF point, bool update)
{
    QPointF coord = mapToCoordinate(point);
    zoom_ = qMin<double>(MAX_ZOOM, qMax<double>(MIN_ZOOM, zoom));
    updatePixelsPerUnit();

    setPosition(coord - QPointF(width()*(point.x()-0.5)/pixelsPerUnit(), height()*(point.y()-0.5)/pixelsPerUnit()));

    if (update)
        repaint();

}
void CircuitView::setZoom(QPointF pointAi, QPointF pointBi, QPointF pointAf, QPointF pointBf, bool update)
{
    QPointF hotSpot = (pointAf + pointBf)/2;

    QVector2D a(width()*(pointAi.x() - pointBi.x()), height()*(pointAi.y() - pointBi.y()));
    QVector2D b(width()*(pointAf.x() - pointBf.x()), height()*(pointAf.y() - pointBf.y()));

    double newZoom = zoom() * b.length() / a.length();
    setZoom(newZoom, hotSpot, update);
}
<|endoftext|>


<commit_before>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2008 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "WrapHelper.h"

#include "../player/BoostPython.h"

#include "../graphics/Bitmap.h"

#include "../base/Point.h"

#include <vector>
#include <sstream>

using namespace boost::python;
using namespace std;
using namespace avg;

namespace DPointHelper
{
    int len(const DPoint&)
    {
        return 2;
    }

    void checkItemRange(int i) {
        if (i!=0 && i!=1) {
            throw std::out_of_range("Index out of range for Point2D. Must be 0 or 1.");
        }
    }
    double getItem(const DPoint& pt, int i)
    {
        checkItemRange(i);
        switch(i) {
            case 0:
                return pt.x;
            case 1:
                return pt.y;
        }
    }
    void setItem(DPoint& pt, int i, double val)
    {
        checkItemRange(i);
        switch(i) {
            case 0:
                pt.x = val;
                break;
            case 1:
                pt.y = val;
                break;
        }
    }

    string str(const DPoint& pt)
    {
        stringstream st;
        st << "(" << pt.x << "," << pt.y << ")";
        return st.str();
    }
    string repr(const DPoint& pt)
    {
        stringstream st;
        st << "Point2D(" << pt.x << "," << pt.y << ")";
        return st.str();
    }
}

void export_bitmap()
{
    from_python_sequence<vector<double>, variable_capacity_policy>();

    class_<DPoint>("Point2D",
            "A point in 2D space. Supports arithmetic operations on vectors.",
            no_init)
        .def(init<>())
        .def(init<double, double>())
        .def(init<vector<double> >())
        .def(init<const DPoint&>())
        .def("__len__", &DPointHelper::len)
        .def("__getitem__", &DPointHelper::getItem)
        .def("__setitem__", &DPointHelper::setItem)
        .def("__str__", &DPointHelper::str)
        .def("__repr__", &DPointHelper::repr)
        .def("normalize", &DPoint::normalize,
                "normalize()\n"
                "Normalizes the point so it's angle stays the same but the norm is one.")
        .def("getNorm", &DPoint::getNorm,
                "getNorm() -> norm\n"
                "Returns the euclidian norm of the point, that is sqrt(x*x+y*y).")
        .def(self == self)
        .def(self != self)
        .def(-self)
        .def(self + self)
        .def(self - self)
        .def(self - self)
        .def(float() * self)
        .def(self * float())
        .def(self / float())
    ;

    enum_<PixelFormat>("pixelformat")
        .value("B5G6R5", B5G6R5)
        .value("B8G8R8", B8G8R8)
        .value("B8G8R8A8", B8G8R8A8)
        .value("B8G8R8X8", B8G8R8X8)
        .value("A8B8G8R8", A8B8G8R8)
        .value("X8B8G8R8", X8B8G8R8)
        .value("R5G6B5", R5G6B5)
        .value("R8G8B8", R8G8B8)
        .value("R8G8B8A8", R8G8B8A8)
        .value("R8G8B8X8", R8G8B8X8)
        .value("A8R8G8B8", A8R8G8B8)
        .value("X8R8G8B8", X8R8G8B8)
        .value("I8", I8)
        .value("YCbCr422", YCbCr422)
        .export_values();

    class_<Bitmap>("Bitmap",
            "Class representing a rectangular set of pixels. Bitmaps can be obtained\n"
            "from any RasterNode. For nodes of type Image, the current bitmap can be\n"
            "set as well.",
            no_init)
        .def(init<IntPoint, PixelFormat, std::string>())
        .def(init<Bitmap>())
        .def(init<std::string>())
        .def("save", &Bitmap::save,
                "save(filename)\n"
                "Writes the image to a file. File format is determined using the\n"
                "extension. Any file format specified by ImageMagick \n"
                "(U{http://www.imagemagick.org}) can be used.")
        .def("getSize", &Bitmap::getSize,
                "getSize()\n\n"
                "Returns the size of the image in pixels.")
        .def("getFormat", &Bitmap::getPixelFormat,
                "getFormat()\n"
                "Returns the layout of the pixels in the bitmap.\n"
                "Possible return values are B5G6R5, B8G8R8, B8G8R8A8, B8G8R8X8,\n"
                "A8B8G8R8, X8B8G8R8, R5G6B5, R8G8B8, R8G8B8A8, R8G8B8X8, A8R8G8B8,\n"
                "X8R8G8B8, I8 and YCbCr422.")
        .def("getPixels", &Bitmap::getPixelsAsString,
                "getPixels()\n"
                "Returns the raw pixel data in the bitmap as a python string. This\n"
                "method can be used to interface to the python imaging library PIL\n"
                "(U{http://www.pythonware.com/products/pil/}).")
        .def("setPixels", &Bitmap::setPixelsFromString,
                "setPixels(pixels)\n\n"
                "Changes the raw pixel data in the bitmap. Doesn't change dimensions \n"
                "or pixel format. Can be used to interface to the python imaging\n"
                "library PIL (U{http://www.pythonware.com/products/pil/}).\n"
                "@param pixels: Image data as a python string.")
        .def("subtract", &Bitmap::subtract,
                return_value_policy<manage_new_object>(),
                "subtract(otherbitmap) -> bmp\n")
        .def("getAvg", &Bitmap::getAvg)
        .def("getStdDev", &Bitmap::getStdDev)
        .def("getName", &Bitmap::getName,
                return_value_policy<copy_const_reference>(),
                "getName() -> string\n\n")
    ;

}
<commit_msg>Point2D: changed getItem implementation to avoid compiler warnings<commit_after>//
//  libavg - Media Playback Engine.
//  Copyright (C) 2003-2008 Ulrich von Zadow
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
//  Current versions can be found at www.libavg.de
//

#include "WrapHelper.h"

#include "../player/BoostPython.h"

#include "../graphics/Bitmap.h"

#include "../base/Point.h"

#include <vector>
#include <sstream>

using namespace boost::python;
using namespace std;
using namespace avg;

namespace DPointHelper
{
    int len(const DPoint&)
    {
        return 2;
    }

    void checkItemRange(int i) {
        if (i!=0 && i!=1) {
            throw std::out_of_range("Index out of range for Point2D. Must be 0 or 1.");
        }
    }
    double getItem(const DPoint& pt, int i)
    {
        checkItemRange(i);
        if (i==0) {
            return pt.x;
        } else {
            return pt.y;
        }
    }
    void setItem(DPoint& pt, int i, double val)
    {
        checkItemRange(i);
        if (i==0) {
            pt.x = val;
        } else {
            pt.y = val;
        }
    }

    string str(const DPoint& pt)
    {
        stringstream st;
        st << "(" << pt.x << "," << pt.y << ")";
        return st.str();
    }
    string repr(const DPoint& pt)
    {
        stringstream st;
        st << "Point2D(" << pt.x << "," << pt.y << ")";
        return st.str();
    }
}

void export_bitmap()
{
    from_python_sequence<vector<double>, variable_capacity_policy>();

    class_<DPoint>("Point2D",
            "A point in 2D space. Supports arithmetic operations on vectors.",
            no_init)
        .def(init<>())
        .def(init<double, double>())
        .def(init<vector<double> >())
        .def(init<const DPoint&>())
        .def("__len__", &DPointHelper::len)
        .def("__getitem__", &DPointHelper::getItem)
        .def("__setitem__", &DPointHelper::setItem)
        .def("__str__", &DPointHelper::str)
        .def("__repr__", &DPointHelper::repr)
        .def("normalize", &DPoint::normalize,
                "normalize()\n"
                "Normalizes the point so it's angle stays the same but the norm is one.")
        .def("getNorm", &DPoint::getNorm,
                "getNorm() -> norm\n"
                "Returns the euclidian norm of the point, that is sqrt(x*x+y*y).")
        .def(self == self)
        .def(self != self)
        .def(-self)
        .def(self + self)
        .def(self - self)
        .def(self - self)
        .def(float() * self)
        .def(self * float())
        .def(self / float())
    ;

    enum_<PixelFormat>("pixelformat")
        .value("B5G6R5", B5G6R5)
        .value("B8G8R8", B8G8R8)
        .value("B8G8R8A8", B8G8R8A8)
        .value("B8G8R8X8", B8G8R8X8)
        .value("A8B8G8R8", A8B8G8R8)
        .value("X8B8G8R8", X8B8G8R8)
        .value("R5G6B5", R5G6B5)
        .value("R8G8B8", R8G8B8)
        .value("R8G8B8A8", R8G8B8A8)
        .value("R8G8B8X8", R8G8B8X8)
        .value("A8R8G8B8", A8R8G8B8)
        .value("X8R8G8B8", X8R8G8B8)
        .value("I8", I8)
        .value("YCbCr422", YCbCr422)
        .export_values();

    class_<Bitmap>("Bitmap",
            "Class representing a rectangular set of pixels. Bitmaps can be obtained\n"
            "from any RasterNode. For nodes of type Image, the current bitmap can be\n"
            "set as well.",
            no_init)
        .def(init<IntPoint, PixelFormat, std::string>())
        .def(init<Bitmap>())
        .def(init<std::string>())
        .def("save", &Bitmap::save,
                "save(filename)\n"
                "Writes the image to a file. File format is determined using the\n"
                "extension. Any file format specified by ImageMagick \n"
                "(U{http://www.imagemagick.org}) can be used.")
        .def("getSize", &Bitmap::getSize,
                "getSize()\n\n"
                "Returns the size of the image in pixels.")
        .def("getFormat", &Bitmap::getPixelFormat,
                "getFormat()\n"
                "Returns the layout of the pixels in the bitmap.\n"
                "Possible return values are B5G6R5, B8G8R8, B8G8R8A8, B8G8R8X8,\n"
                "A8B8G8R8, X8B8G8R8, R5G6B5, R8G8B8, R8G8B8A8, R8G8B8X8, A8R8G8B8,\n"
                "X8R8G8B8, I8 and YCbCr422.")
        .def("getPixels", &Bitmap::getPixelsAsString,
                "getPixels()\n"
                "Returns the raw pixel data in the bitmap as a python string. This\n"
                "method can be used to interface to the python imaging library PIL\n"
                "(U{http://www.pythonware.com/products/pil/}).")
        .def("setPixels", &Bitmap::setPixelsFromString,
                "setPixels(pixels)\n\n"
                "Changes the raw pixel data in the bitmap. Doesn't change dimensions \n"
                "or pixel format. Can be used to interface to the python imaging\n"
                "library PIL (U{http://www.pythonware.com/products/pil/}).\n"
                "@param pixels: Image data as a python string.")
        .def("subtract", &Bitmap::subtract,
                return_value_policy<manage_new_object>(),
                "subtract(otherbitmap) -> bmp\n")
        .def("getAvg", &Bitmap::getAvg)
        .def("getStdDev", &Bitmap::getStdDev)
        .def("getName", &Bitmap::getName,
                return_value_policy<copy_const_reference>(),
                "getName() -> string\n\n")
    ;

}
<|endoftext|>


<commit_before>#ifndef sw_x0_fileinfo_service_hpp
#define sw_x0_fileinfo_service_hpp (1)

#include <x0/fileinfo.hpp>
#include <x0/cache.hpp>
#include <x0/types.hpp>
#include <x0/api.hpp>

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/signal.hpp>

#include <sys/inotify.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#if 0
#	define FILEINFO_DEBUG(msg...) printf("fileinfo_service: " msg)
#else
#	define FILEINFO_DEBUG(msg...) /*!*/
#endif

namespace x0 {

/** service for retrieving file information.
 *
 * This is like stat(), in fact, it's using stat() and more magic, but
 * caches the result for further use and also invalidates in realtime the file-info items
 * in case their underlying inode has been updated.
 *
 * \note this class is not thread-safe
 */
class X0_API fileinfo_service :
	public boost::noncopyable
{
private:
	boost::asio::posix::stream_descriptor in_;		//!< inotify handle used for invalidating updated stat entries
	std::map<std::string, fileinfo_ptr> cache_;		//!< cache, storing path->fileinfo pairs

	std::map<int, std::string> wd_;					//!< stores wd->path/fileinfo pairs - if someone knows how to eliminate this extra map, tell me.
	boost::array<char, 8192> inbuf_;				//!< internal read-buffer for retrieving inotify events
	inotify_event ev_;								//!< used to store a new inotify event

	bool etag_consider_mtime_;						//!< flag, specifying wether or not the file modification-time is part of the ETag
	bool etag_consider_size_;						//!< flag, specifying wether or not the file size is part of the ETag
	bool etag_consider_inode_;						//!< flag, specifying wether or not the file inode number is part of the ETag

	std::map<std::string, std::string> mimetypes_;	//!< cached database for file extension to mimetype mapping
	std::string default_mimetype_;					//!< default mimetype for those files we could not determine the mimetype.

public:
	explicit fileinfo_service(boost::asio::io_service& io);
	~fileinfo_service();

	boost::signal<void(const std::string& filename, fileinfo_ptr info)> on_invalidate;

	fileinfo_ptr query(const std::string& filename);
	fileinfo_ptr operator()(const std::string& filename);

	std::size_t size() const;
	bool empty() const;

	bool etag_consider_mtime() const;
	void etag_consider_mtime(bool value);

	bool etag_consider_size() const;
	void etag_consider_size(bool value);

	bool etag_consider_inode() const;
	void etag_consider_inode(bool value);

	void load_mimetypes(const std::string& filename);

	std::string default_mimetype() const;
	void default_mimetype(const std::string& value);

private:
	std::string get_mimetype(const std::string& ext) const;
	std::string make_etag(const fileinfo& fi) const;

	void async_read();

	void invalidate(const boost::system::error_code& ec, std::size_t bytes_transferred)
	{
		FILEINFO_DEBUG("invalidate(ec=%s, bt=%ld)\n", ec.message().c_str(), bytes_transferred);
		inotify_event *ev = reinterpret_cast<inotify_event *>(inbuf_.data());
		inotify_event *ee = ev + bytes_transferred;

		while (ev < ee && ev->wd != 0)
		{
			FILEINFO_DEBUG("--invalidate(len=%d, fn=%s)\n", ev->len, ev->name);
			auto i = wd_.find(ev->wd);

			if (i != wd_.end())
			{
				FILEINFO_DEBUG("--invalidate: wd: %d, remove: %s\n", ev->wd, i->second.c_str());

				auto k = cache_.find(i->second);
				on_invalidate(k->first, k->second);

				cache_.erase(k);
				wd_.erase(i);
			}
			ev += sizeof(*ev) + ev->len;
		}

		if (!ec)
		{
			async_read();
		}
	}
};

inline fileinfo_service::fileinfo_service(boost::asio::io_service& io) :
	in_(io, ::inotify_init()),
	cache_(),
	wd_(),
	inbuf_(),
	// ev_(),
	etag_consider_mtime_(true),
	etag_consider_size_(true),
	etag_consider_inode_(false),
	mimetypes_(),
	default_mimetype_("text/plain")
{
	if (::fcntl(in_.native(), F_SETFL, O_NONBLOCK) == -1)
		FILEINFO_DEBUG("fcntl(O_NONBLOCK): %s", strerror(errno));

	if (::fcntl(in_.native(), F_SETFD, FD_CLOEXEC) == -1)
		FILEINFO_DEBUG("fcntl(FD_CLOEXEC): %s", strerror(errno));

	async_read();
}

inline void fileinfo_service::async_read()
{
	FILEINFO_DEBUG("(re-)assign async_read (watches=%ld)\n", size());

	boost::asio::async_read(in_, boost::asio::buffer(inbuf_),
		boost::asio::transfer_at_least(sizeof(inotify_event)),
		boost::bind(&fileinfo_service::invalidate, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

inline fileinfo_service::~fileinfo_service()
{
}

inline fileinfo_ptr fileinfo_service::query(const std::string& _filename)
{
	std::string filename(_filename[_filename.size() - 1] == '/' ? _filename.substr(0, _filename.size() - 1) : _filename);

	auto i = cache_.find(filename);
	if (i != cache_.end())
	{
		FILEINFO_DEBUG("query.cached(%s)\n", filename.c_str());
		return i->second;
	}

	if (fileinfo_ptr fi = fileinfo_ptr(new fileinfo(filename)))
	{
		fi->mimetype_ = get_mimetype(filename);
		fi->etag_ = make_etag(*fi);

		int rv = ::inotify_add_watch(in_.native(), filename.c_str(),
			IN_ONESHOT | IN_ATTRIB | IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT);

		if (rv == 0)
		{
			cache_[filename] = fi;
			FILEINFO_DEBUG("query(%s) wd=%d\n", filename.c_str(), rv);
			wd_[rv] = filename;
		}

		return fi;
	}

	FILEINFO_DEBUG("query(%s) failed (%s)\n", filename.c_str(), strerror(errno));
	// either ::stat() or caching failed.

	return fileinfo_ptr();
}

inline fileinfo_ptr fileinfo_service::operator()(const std::string& filename)
{
	return query(filename);
}

inline std::size_t fileinfo_service::size() const
{
	return cache_.size();
}

inline bool fileinfo_service::empty() const
{
	return cache_.empty();
}

inline bool fileinfo_service::etag_consider_mtime() const
{
	return etag_consider_mtime_;
}

inline void fileinfo_service::etag_consider_mtime(bool value)
{
	etag_consider_mtime_ = value;
}

inline bool fileinfo_service::etag_consider_size() const
{
	return etag_consider_size_;
}

inline void fileinfo_service::etag_consider_size(bool value)
{
	etag_consider_size_ = value;
}

inline bool fileinfo_service::etag_consider_inode() const
{
	return etag_consider_inode_;
}

inline void fileinfo_service::etag_consider_inode(bool value)
{
	etag_consider_inode_ = value;
}

inline std::string fileinfo_service::default_mimetype() const
{
	return default_mimetype_;
}

inline void fileinfo_service::default_mimetype(const std::string& value)
{
	default_mimetype_ = value;
}

inline std::string fileinfo_service::get_mimetype(const std::string& filename) const
{
	std::size_t ndot = filename.find_last_of(".");
	std::size_t nslash = filename.find_last_of("/");

	if (ndot != std::string::npos && ndot > nslash)
	{
		std::string ext(filename.substr(ndot + 1));

		while (ext.size())
		{
			auto i = mimetypes_.find(ext);

			if (i != mimetypes_.end())
				return i->second;

			if (ext[ext.size() - 1] != '~')
				break;

			ext.resize(ext.size() - 1);
		}
	}

	return default_mimetype_;
}

inline std::string fileinfo_service::make_etag(const fileinfo& fi) const
{
	int count = 0;
	std::stringstream sstr;

	sstr << '"';

	if (etag_consider_mtime_)
	{
		if (count++) sstr << '-';
		sstr << fi.st_.st_mtime;
	}

	if (etag_consider_size_)
	{
		if (count++) sstr << '-';
		sstr << fi.st_.st_size;
	}

	if (etag_consider_inode_)
	{
		if (count++) sstr << '-';
		sstr << fi.st_.st_ino;
	}

	/// \todo support checksum etags (crc, md5, sha1, ...) - although, btrfs supports checksums directly on filesystem level!

	sstr << '"';

	return sstr.str();
}

} // namespace x0

#endif
<commit_msg>core: fixes cashing-bug wrt inotify.<commit_after>#ifndef sw_x0_fileinfo_service_hpp
#define sw_x0_fileinfo_service_hpp (1)

#include <x0/fileinfo.hpp>
#include <x0/cache.hpp>
#include <x0/types.hpp>
#include <x0/api.hpp>

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/signal.hpp>

#include <sys/inotify.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#if 0
#	define FILEINFO_DEBUG(msg...) printf("fileinfo_service: " msg)
#else
#	define FILEINFO_DEBUG(msg...) /*!*/
#endif

namespace x0 {

/** service for retrieving file information.
 *
 * This is like stat(), in fact, it's using stat() and more magic, but
 * caches the result for further use and also invalidates in realtime the file-info items
 * in case their underlying inode has been updated.
 *
 * \note this class is not thread-safe
 */
class X0_API fileinfo_service :
	public boost::noncopyable
{
private:
	boost::asio::posix::stream_descriptor in_;		//!< inotify handle used for invalidating updated stat entries
	std::map<std::string, fileinfo_ptr> cache_;		//!< cache, storing path->fileinfo pairs

	std::map<int, std::string> wd_;					//!< stores wd->path/fileinfo pairs - if someone knows how to eliminate this extra map, tell me.
	boost::array<char, 8192> inbuf_;				//!< internal read-buffer for retrieving inotify events
	inotify_event ev_;								//!< used to store a new inotify event

	bool etag_consider_mtime_;						//!< flag, specifying wether or not the file modification-time is part of the ETag
	bool etag_consider_size_;						//!< flag, specifying wether or not the file size is part of the ETag
	bool etag_consider_inode_;						//!< flag, specifying wether or not the file inode number is part of the ETag

	std::map<std::string, std::string> mimetypes_;	//!< cached database for file extension to mimetype mapping
	std::string default_mimetype_;					//!< default mimetype for those files we could not determine the mimetype.

public:
	explicit fileinfo_service(boost::asio::io_service& io);
	~fileinfo_service();

	boost::signal<void(const std::string& filename, fileinfo_ptr info)> on_invalidate;

	fileinfo_ptr query(const std::string& filename);
	fileinfo_ptr operator()(const std::string& filename);

	std::size_t size() const;
	bool empty() const;

	bool etag_consider_mtime() const;
	void etag_consider_mtime(bool value);

	bool etag_consider_size() const;
	void etag_consider_size(bool value);

	bool etag_consider_inode() const;
	void etag_consider_inode(bool value);

	void load_mimetypes(const std::string& filename);

	std::string default_mimetype() const;
	void default_mimetype(const std::string& value);

private:
	std::string get_mimetype(const std::string& ext) const;
	std::string make_etag(const fileinfo& fi) const;

	void async_read();

	void invalidate(const boost::system::error_code& ec, std::size_t bytes_transferred)
	{
		FILEINFO_DEBUG("invalidate(ec=%s, bt=%ld)\n", ec.message().c_str(), bytes_transferred);
		inotify_event *ev = reinterpret_cast<inotify_event *>(inbuf_.data());
		inotify_event *ee = ev + bytes_transferred;

		while (ev < ee && ev->wd != 0)
		{
			FILEINFO_DEBUG("--invalidate(len=%d, fn=%s)\n", ev->len, ev->name);
			auto i = wd_.find(ev->wd);

			if (i != wd_.end())
			{
				FILEINFO_DEBUG("--invalidate: wd: %d, remove: %s\n", ev->wd, i->second.c_str());

				auto k = cache_.find(i->second);
				on_invalidate(k->first, k->second);

				cache_.erase(k);
				wd_.erase(i);
			}
			ev += sizeof(*ev) + ev->len;
		}

		if (!ec)
		{
			async_read();
		}
	}
};

inline fileinfo_service::fileinfo_service(boost::asio::io_service& io) :
	in_(io, ::inotify_init()),
	cache_(),
	wd_(),
	inbuf_(),
	// ev_(),
	etag_consider_mtime_(true),
	etag_consider_size_(true),
	etag_consider_inode_(false),
	mimetypes_(),
	default_mimetype_("text/plain")
{
	if (::fcntl(in_.native(), F_SETFL, O_NONBLOCK) == -1)
		FILEINFO_DEBUG("fcntl(O_NONBLOCK): %s", strerror(errno));

	if (::fcntl(in_.native(), F_SETFD, FD_CLOEXEC) == -1)
		FILEINFO_DEBUG("fcntl(FD_CLOEXEC): %s", strerror(errno));

	async_read();
}

inline void fileinfo_service::async_read()
{
	FILEINFO_DEBUG("(re-)assign async_read (watches=%ld)\n", size());

	boost::asio::async_read(in_, boost::asio::buffer(inbuf_),
		boost::asio::transfer_at_least(sizeof(inotify_event)),
		boost::bind(&fileinfo_service::invalidate, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

inline fileinfo_service::~fileinfo_service()
{
}

inline fileinfo_ptr fileinfo_service::query(const std::string& _filename)
{
	std::string filename(_filename[_filename.size() - 1] == '/' ? _filename.substr(0, _filename.size() - 1) : _filename);

	auto i = cache_.find(filename);
	if (i != cache_.end())
	{
		FILEINFO_DEBUG("query.cached(%s)\n", filename.c_str());
		return i->second;
	}

	if (fileinfo_ptr fi = fileinfo_ptr(new fileinfo(filename)))
	{
		fi->mimetype_ = get_mimetype(filename);
		fi->etag_ = make_etag(*fi);

		int rv = ::inotify_add_watch(in_.native(), filename.c_str(),
			IN_ONESHOT | IN_ATTRIB | IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT);

		if (rv != -1)
		{
			cache_[filename] = fi;
			FILEINFO_DEBUG("query(%s) wd=%d\n", filename.c_str(), rv);
			wd_[rv] = filename;
		}
		else
		{
			FILEINFO_DEBUG("query(%s) inotify error: %s\n", filename.c_str(), strerror(errno));
		}

		return fi;
	}

	FILEINFO_DEBUG("query(%s) failed (%s)\n", filename.c_str(), strerror(errno));
	// either ::stat() or caching failed.

	return fileinfo_ptr();
}

inline fileinfo_ptr fileinfo_service::operator()(const std::string& filename)
{
	return query(filename);
}

inline std::size_t fileinfo_service::size() const
{
	return cache_.size();
}

inline bool fileinfo_service::empty() const
{
	return cache_.empty();
}

inline bool fileinfo_service::etag_consider_mtime() const
{
	return etag_consider_mtime_;
}

inline void fileinfo_service::etag_consider_mtime(bool value)
{
	etag_consider_mtime_ = value;
}

inline bool fileinfo_service::etag_consider_size() const
{
	return etag_consider_size_;
}

inline void fileinfo_service::etag_consider_size(bool value)
{
	etag_consider_size_ = value;
}

inline bool fileinfo_service::etag_consider_inode() const
{
	return etag_consider_inode_;
}

inline void fileinfo_service::etag_consider_inode(bool value)
{
	etag_consider_inode_ = value;
}

inline std::string fileinfo_service::default_mimetype() const
{
	return default_mimetype_;
}

inline void fileinfo_service::default_mimetype(const std::string& value)
{
	default_mimetype_ = value;
}

inline std::string fileinfo_service::get_mimetype(const std::string& filename) const
{
	std::size_t ndot = filename.find_last_of(".");
	std::size_t nslash = filename.find_last_of("/");

	if (ndot != std::string::npos && ndot > nslash)
	{
		std::string ext(filename.substr(ndot + 1));

		while (ext.size())
		{
			auto i = mimetypes_.find(ext);

			if (i != mimetypes_.end())
				return i->second;

			if (ext[ext.size() - 1] != '~')
				break;

			ext.resize(ext.size() - 1);
		}
	}

	return default_mimetype_;
}

inline std::string fileinfo_service::make_etag(const fileinfo& fi) const
{
	int count = 0;
	std::stringstream sstr;

	sstr << '"';

	if (etag_consider_mtime_)
	{
		if (count++) sstr << '-';
		sstr << fi.st_.st_mtime;
	}

	if (etag_consider_size_)
	{
		if (count++) sstr << '-';
		sstr << fi.st_.st_size;
	}

	if (etag_consider_inode_)
	{
		if (count++) sstr << '-';
		sstr << fi.st_.st_ino;
	}

	/// \todo support checksum etags (crc, md5, sha1, ...) - although, btrfs supports checksums directly on filesystem level!

	sstr << '"';

	return sstr.str();
}

} // namespace x0

#endif
<|endoftext|>


<commit_before>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2020 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/app/discord/discord_presence.h"
#include "xenia/app/emulator_window.h"
#include "xenia/base/cvar.h"
#include "xenia/base/debugging.h"
#include "xenia/base/logging.h"
#include "xenia/base/main.h"
#include "xenia/base/profiling.h"
#include "xenia/base/threading.h"
#include "xenia/config.h"
#include "xenia/debug/ui/debug_window.h"
#include "xenia/emulator.h"
#include "xenia/ui/file_picker.h"
#include "xenia/vfs/devices/host_path_device.h"

// Available audio systems:
#include "xenia/apu/nop/nop_audio_system.h"
#include "xenia/apu/sdl/sdl_audio_system.h"
#if XE_PLATFORM_WIN32
#include "xenia/apu/xaudio2/xaudio2_audio_system.h"
#endif  // XE_PLATFORM_WIN32

// Available graphics systems:
#include "xenia/gpu/null/null_graphics_system.h"
#include "xenia/gpu/vk/vulkan_graphics_system.h"
#include "xenia/gpu/vulkan/vulkan_graphics_system.h"
#if XE_PLATFORM_WIN32
#include "xenia/gpu/d3d12/d3d12_graphics_system.h"
#endif  // XE_PLATFORM_WIN32

// Available input drivers:
#include "xenia/hid/nop/nop_hid.h"
#include "xenia/hid/sdl/sdl_hid.h"
#if XE_PLATFORM_WIN32
#include "xenia/hid/winkey/winkey_hid.h"
#include "xenia/hid/xinput/xinput_hid.h"
#endif  // XE_PLATFORM_WIN32

#include "third_party/xbyak/xbyak/xbyak_util.h"

DEFINE_string(apu, "any", "Audio system. Use: [any, nop, sdl, xaudio2]", "APU");
DEFINE_string(gpu, "any",
              "Graphics system. Use: [any, d3d12, vulkan, vk, null]", "GPU");
DEFINE_string(hid, "any", "Input system. Use: [any, nop, sdl, winkey, xinput]",
              "HID");

DEFINE_bool(fullscreen, false, "Toggles fullscreen", "GPU");

DEFINE_string(content_root, "", "Root path for content (save/etc) storage.",
              "Storage");

DEFINE_bool(mount_scratch, false, "Enable scratch mount", "Storage");
DEFINE_bool(mount_cache, false, "Enable cache mount", "Storage");

DEFINE_transient_string(target, "",
                        "Specifies the target .xex or .iso to execute.",
                        "General");
DECLARE_bool(debug);

DEFINE_bool(discord, true, "Enable Discord rich presence", "General");

namespace xe {
namespace app {

template <typename T, typename... Args>
class Factory {
 private:
  struct Creator {
    std::string name;
    std::function<bool()> is_available;
    std::function<std::unique_ptr<T>(Args...)> instantiate;
  };

  std::vector<Creator> creators_;

 public:
  void Add(const std::string& name, std::function<bool()> is_available,
           std::function<std::unique_ptr<T>(Args...)> instantiate) {
    creators_.push_back({name, is_available, instantiate});
  }

  void Add(const std::string& name,
           std::function<std::unique_ptr<T>(Args...)> instantiate) {
    constexpr auto always_available = []() { return true; };
    Add(name, always_available, instantiate);
  }

  template <typename DT>
  void Add(const std::string& name) {
    Add(name, DT::IsAvailable, [](Args... args) {
      return std::make_unique<DT>(std::forward<Args>(args)...);
    });
  }

  std::unique_ptr<T> Create(const std::string& name, Args... args) {
    if (!name.empty() && name != "any") {
      auto it = std::find_if(
          creators_.cbegin(), creators_.cend(),
          [&name](const auto& f) { return name.compare(f.name) == 0; });
      if (it != creators_.cend() && (*it).is_available()) {
        return (*it).instantiate(std::forward<Args>(args)...);
      }
      return nullptr;
    } else {
      for (const auto& creator : creators_) {
        if (!creator.is_available()) continue;
        auto instance = creator.instantiate(std::forward<Args>(args)...);
        if (!instance) continue;
        return instance;
      }
      return nullptr;
    }
  }

  std::vector<std::unique_ptr<T>> CreateAll(const std::string& name,
                                            Args... args) {
    std::vector<std::unique_ptr<T>> instances;
    if (!name.empty() && name != "any") {
      auto it = std::find_if(
          creators_.cbegin(), creators_.cend(),
          [&name](const auto& f) { return name.compare(f.name) == 0; });
      if (it != creators_.cend() && (*it).is_available()) {
        auto instance = (*it).instantiate(std::forward<Args>(args)...);
        if (instance) {
          instances.emplace_back(std::move(instance));
        }
      }
    } else {
      for (const auto& creator : creators_) {
        if (!creator.is_available()) continue;
        auto instance = creator.instantiate(std::forward<Args>(args)...);
        if (instance) {
          instances.emplace_back(std::move(instance));
        }
      }
    }
    return instances;
  }
};

std::unique_ptr<apu::AudioSystem> CreateAudioSystem(cpu::Processor* processor) {
  Factory<apu::AudioSystem, cpu::Processor*> factory;
#if XE_PLATFORM_WIN32
  factory.Add<apu::xaudio2::XAudio2AudioSystem>("xaudio2");
#endif  // XE_PLATFORM_WIN32
  factory.Add<apu::sdl::SDLAudioSystem>("sdl");
  factory.Add<apu::nop::NopAudioSystem>("nop");
  return factory.Create(cvars::apu, processor);
}

std::unique_ptr<gpu::GraphicsSystem> CreateGraphicsSystem() {
  Factory<gpu::GraphicsSystem> factory;
#if XE_PLATFORM_WIN32
  factory.Add<gpu::d3d12::D3D12GraphicsSystem>("d3d12");
#endif  // XE_PLATFORM_WIN32
  // Abandoned Vulkan graphics system.
  factory.Add<gpu::vulkan::VulkanGraphicsSystem>("vulkan");
  // New Vulkan graphics system.
  // TODO(Triang3l): Move this higher when it's more ready, then drop the old
  // Vulkan graphics system.
  factory.Add<gpu::vk::VulkanGraphicsSystem>("vk");
  factory.Add<gpu::null::NullGraphicsSystem>("null");
  return factory.Create(cvars::gpu);
}

std::vector<std::unique_ptr<hid::InputDriver>> CreateInputDrivers(
    ui::Window* window) {
  std::vector<std::unique_ptr<hid::InputDriver>> drivers;
  if (cvars::hid.compare("nop") == 0) {
    drivers.emplace_back(xe::hid::nop::Create(window));
  } else {
    Factory<hid::InputDriver, ui::Window*> factory;
#if XE_PLATFORM_WIN32
    factory.Add("xinput", xe::hid::xinput::Create);
    // WinKey input driver should always be the last input driver added!
    factory.Add("winkey", xe::hid::winkey::Create);
#endif  // XE_PLATFORM_WIN32
    factory.Add("sdl", xe::hid::sdl::Create);
    for (auto& driver : factory.CreateAll(cvars::hid, window)) {
      if (XSUCCEEDED(driver->Setup())) {
        drivers.emplace_back(std::move(driver));
      }
    }
    if (drivers.empty()) {
      // Fallback to nop if none created.
      drivers.emplace_back(xe::hid::nop::Create(window));
    }
  }
  return drivers;
}

int xenia_main(const std::vector<std::wstring>& args) {
  Profiler::Initialize();
  Profiler::ThreadEnter("main");

  // Figure out where content should go.
  std::wstring content_root = xe::to_wstring(cvars::content_root);
  std::wstring config_folder;

  if (content_root.empty()) {
    auto base_path = xe::filesystem::GetExecutableFolder();
    base_path = xe::to_absolute_path(base_path);

    auto portable_path = xe::join_paths(base_path, L"portable.txt");
    if (xe::filesystem::PathExists(portable_path)) {
      content_root = xe::join_paths(base_path, L"content");
      config_folder = base_path;
    } else {
      content_root = xe::filesystem::GetUserFolder();
#if defined(XE_PLATFORM_WIN32)
      content_root = xe::join_paths(content_root, L"Xenia");
#elif defined(XE_PLATFORM_LINUX)
      content_root = xe::join_paths(content_root, L"Xenia");
#else
#warning Unhandled platform for content root.
      content_root = xe::join_paths(content_root, L"Xenia");
#endif
      config_folder = content_root;
      content_root = xe::join_paths(content_root, L"content");
    }
  }
  content_root = xe::to_absolute_path(content_root);

  XELOGI("Content root: %S", content_root.c_str());
  config::SetupConfig(config_folder);

  if (cvars::discord) {
    discord::DiscordPresence::Initialize();
    discord::DiscordPresence::NotPlaying();
  }

  // Create the emulator but don't initialize so we can setup the window.
  auto emulator = std::make_unique<Emulator>(L"", content_root);

  // Main emulator display window.
  auto emulator_window = EmulatorWindow::Create(emulator.get());

  // Setup and initialize all subsystems. If we can't do something
  // (unsupported system, memory issues, etc) this will fail early.
  X_STATUS result =
      emulator->Setup(emulator_window->window(), CreateAudioSystem,
                      CreateGraphicsSystem, CreateInputDrivers);
  if (XFAILED(result)) {
    XELOGE("Failed to setup emulator: %.8X", result);
    return 1;
  }

  if (cvars::mount_scratch) {
    auto scratch_device = std::make_unique<xe::vfs::HostPathDevice>(
        "\\SCRATCH", L"scratch", false);
    if (!scratch_device->Initialize()) {
      XELOGE("Unable to scan scratch path");
    } else {
      if (!emulator->file_system()->RegisterDevice(std::move(scratch_device))) {
        XELOGE("Unable to register scratch path");
      } else {
        emulator->file_system()->RegisterSymbolicLink("scratch:", "\\SCRATCH");
      }
    }
  }

  if (cvars::mount_cache) {
    auto cache0_device =
        std::make_unique<xe::vfs::HostPathDevice>("\\CACHE0", L"cache0", false);
    if (!cache0_device->Initialize()) {
      XELOGE("Unable to scan cache0 path");
    } else {
      if (!emulator->file_system()->RegisterDevice(std::move(cache0_device))) {
        XELOGE("Unable to register cache0 path");
      } else {
        emulator->file_system()->RegisterSymbolicLink("cache0:", "\\CACHE0");
      }
    }

    auto cache1_device =
        std::make_unique<xe::vfs::HostPathDevice>("\\CACHE1", L"cache1", false);
    if (!cache1_device->Initialize()) {
      XELOGE("Unable to scan cache1 path");
    } else {
      if (!emulator->file_system()->RegisterDevice(std::move(cache1_device))) {
        XELOGE("Unable to register cache1 path");
      } else {
        emulator->file_system()->RegisterSymbolicLink("cache1:", "\\CACHE1");
      }
    }
  }

  // Set a debug handler.
  // This will respond to debugging requests so we can open the debug UI.
  std::unique_ptr<xe::debug::ui::DebugWindow> debug_window;
  if (cvars::debug) {
    emulator->processor()->set_debug_listener_request_handler(
        [&](xe::cpu::Processor* processor) {
          if (debug_window) {
            return debug_window.get();
          }
          emulator_window->loop()->PostSynchronous([&]() {
            debug_window = xe::debug::ui::DebugWindow::Create(
                emulator.get(), emulator_window->loop());
            debug_window->window()->on_closed.AddListener(
                [&](xe::ui::UIEvent* e) {
                  emulator->processor()->set_debug_listener(nullptr);
                  emulator_window->loop()->Post(
                      [&]() { debug_window.reset(); });
                });
          });
          return debug_window.get();
        });
  }

  auto evt = xe::threading::Event::CreateAutoResetEvent(false);
  emulator->on_launch.AddListener([&](auto title_id, const auto& game_title) {
    if (cvars::discord) {
      discord::DiscordPresence::PlayingTitle(
          game_title.empty() ? L"Unknown Title" : game_title);
    }
    emulator_window->UpdateTitle();
    evt->Set();
  });

  emulator->on_terminate.AddListener([&]() {
    if (cvars::discord) {
      discord::DiscordPresence::NotPlaying();
    }
  });

  emulator_window->window()->on_closing.AddListener([&](ui::UIEvent* e) {
    // This needs to shut down before the graphics context.
    Profiler::Shutdown();
  });

  bool exiting = false;
  emulator_window->loop()->on_quit.AddListener([&](ui::UIEvent* e) {
    exiting = true;
    evt->Set();

    if (cvars::discord) {
      discord::DiscordPresence::Shutdown();
    }

    // TODO(DrChat): Remove this code and do a proper exit.
    XELOGI("Cheap-skate exit!");
    exit(0);
  });

  // Enable the main menu now that the emulator is properly loaded
  emulator_window->window()->EnableMainMenu();

  // Grab path from the flag or unnamed argument.
  std::wstring path;
  if (!cvars::target.empty()) {
    path = xe::to_wstring(cvars::target);
  }

  // Toggles fullscreen
  if (cvars::fullscreen) emulator_window->ToggleFullscreen();

  if (!path.empty()) {
    // Normalize the path and make absolute.
    std::wstring abs_path = xe::to_absolute_path(path);
    result = emulator->LaunchPath(abs_path);
    if (XFAILED(result)) {
      xe::FatalError("Failed to launch target: %.8X", result);
      emulator.reset();
      emulator_window.reset();
      return 1;
    }
  }

  // Now, we're going to use the main thread to drive events related to
  // emulation.
  while (!exiting) {
    xe::threading::Wait(evt.get(), false);

    while (true) {
      emulator->WaitUntilExit();
      if (emulator->TitleRequested()) {
        emulator->LaunchNextTitle();
      } else {
        break;
      }
    }
  }

  debug_window.reset();
  emulator.reset();

  if (cvars::discord) {
    discord::DiscordPresence::Shutdown();
  }

  Profiler::Dump();
  Profiler::Shutdown();
  emulator_window.reset();
  return 0;
}

}  // namespace app
}  // namespace xe

DEFINE_ENTRY_POINT(L"xenia", xe::app::xenia_main, "[Path to .iso/.xex]",
                   "target");
<commit_msg>[App] Remove inadvertent constexpr.<commit_after>/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2020 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#include "xenia/app/discord/discord_presence.h"
#include "xenia/app/emulator_window.h"
#include "xenia/base/cvar.h"
#include "xenia/base/debugging.h"
#include "xenia/base/logging.h"
#include "xenia/base/main.h"
#include "xenia/base/profiling.h"
#include "xenia/base/threading.h"
#include "xenia/config.h"
#include "xenia/debug/ui/debug_window.h"
#include "xenia/emulator.h"
#include "xenia/ui/file_picker.h"
#include "xenia/vfs/devices/host_path_device.h"

// Available audio systems:
#include "xenia/apu/nop/nop_audio_system.h"
#include "xenia/apu/sdl/sdl_audio_system.h"
#if XE_PLATFORM_WIN32
#include "xenia/apu/xaudio2/xaudio2_audio_system.h"
#endif  // XE_PLATFORM_WIN32

// Available graphics systems:
#include "xenia/gpu/null/null_graphics_system.h"
#include "xenia/gpu/vk/vulkan_graphics_system.h"
#include "xenia/gpu/vulkan/vulkan_graphics_system.h"
#if XE_PLATFORM_WIN32
#include "xenia/gpu/d3d12/d3d12_graphics_system.h"
#endif  // XE_PLATFORM_WIN32

// Available input drivers:
#include "xenia/hid/nop/nop_hid.h"
#include "xenia/hid/sdl/sdl_hid.h"
#if XE_PLATFORM_WIN32
#include "xenia/hid/winkey/winkey_hid.h"
#include "xenia/hid/xinput/xinput_hid.h"
#endif  // XE_PLATFORM_WIN32

#include "third_party/xbyak/xbyak/xbyak_util.h"

DEFINE_string(apu, "any", "Audio system. Use: [any, nop, sdl, xaudio2]", "APU");
DEFINE_string(gpu, "any",
              "Graphics system. Use: [any, d3d12, vulkan, vk, null]", "GPU");
DEFINE_string(hid, "any", "Input system. Use: [any, nop, sdl, winkey, xinput]",
              "HID");

DEFINE_bool(fullscreen, false, "Toggles fullscreen", "GPU");

DEFINE_string(content_root, "", "Root path for content (save/etc) storage.",
              "Storage");

DEFINE_bool(mount_scratch, false, "Enable scratch mount", "Storage");
DEFINE_bool(mount_cache, false, "Enable cache mount", "Storage");

DEFINE_transient_string(target, "",
                        "Specifies the target .xex or .iso to execute.",
                        "General");
DECLARE_bool(debug);

DEFINE_bool(discord, true, "Enable Discord rich presence", "General");

namespace xe {
namespace app {

template <typename T, typename... Args>
class Factory {
 private:
  struct Creator {
    std::string name;
    std::function<bool()> is_available;
    std::function<std::unique_ptr<T>(Args...)> instantiate;
  };

  std::vector<Creator> creators_;

 public:
  void Add(const std::string& name, std::function<bool()> is_available,
           std::function<std::unique_ptr<T>(Args...)> instantiate) {
    creators_.push_back({name, is_available, instantiate});
  }

  void Add(const std::string& name,
           std::function<std::unique_ptr<T>(Args...)> instantiate) {
    auto always_available = []() { return true; };
    Add(name, always_available, instantiate);
  }

  template <typename DT>
  void Add(const std::string& name) {
    Add(name, DT::IsAvailable, [](Args... args) {
      return std::make_unique<DT>(std::forward<Args>(args)...);
    });
  }

  std::unique_ptr<T> Create(const std::string& name, Args... args) {
    if (!name.empty() && name != "any") {
      auto it = std::find_if(
          creators_.cbegin(), creators_.cend(),
          [&name](const auto& f) { return name.compare(f.name) == 0; });
      if (it != creators_.cend() && (*it).is_available()) {
        return (*it).instantiate(std::forward<Args>(args)...);
      }
      return nullptr;
    } else {
      for (const auto& creator : creators_) {
        if (!creator.is_available()) continue;
        auto instance = creator.instantiate(std::forward<Args>(args)...);
        if (!instance) continue;
        return instance;
      }
      return nullptr;
    }
  }

  std::vector<std::unique_ptr<T>> CreateAll(const std::string& name,
                                            Args... args) {
    std::vector<std::unique_ptr<T>> instances;
    if (!name.empty() && name != "any") {
      auto it = std::find_if(
          creators_.cbegin(), creators_.cend(),
          [&name](const auto& f) { return name.compare(f.name) == 0; });
      if (it != creators_.cend() && (*it).is_available()) {
        auto instance = (*it).instantiate(std::forward<Args>(args)...);
        if (instance) {
          instances.emplace_back(std::move(instance));
        }
      }
    } else {
      for (const auto& creator : creators_) {
        if (!creator.is_available()) continue;
        auto instance = creator.instantiate(std::forward<Args>(args)...);
        if (instance) {
          instances.emplace_back(std::move(instance));
        }
      }
    }
    return instances;
  }
};

std::unique_ptr<apu::AudioSystem> CreateAudioSystem(cpu::Processor* processor) {
  Factory<apu::AudioSystem, cpu::Processor*> factory;
#if XE_PLATFORM_WIN32
  factory.Add<apu::xaudio2::XAudio2AudioSystem>("xaudio2");
#endif  // XE_PLATFORM_WIN32
  factory.Add<apu::sdl::SDLAudioSystem>("sdl");
  factory.Add<apu::nop::NopAudioSystem>("nop");
  return factory.Create(cvars::apu, processor);
}

std::unique_ptr<gpu::GraphicsSystem> CreateGraphicsSystem() {
  Factory<gpu::GraphicsSystem> factory;
#if XE_PLATFORM_WIN32
  factory.Add<gpu::d3d12::D3D12GraphicsSystem>("d3d12");
#endif  // XE_PLATFORM_WIN32
  // Abandoned Vulkan graphics system.
  factory.Add<gpu::vulkan::VulkanGraphicsSystem>("vulkan");
  // New Vulkan graphics system.
  // TODO(Triang3l): Move this higher when it's more ready, then drop the old
  // Vulkan graphics system.
  factory.Add<gpu::vk::VulkanGraphicsSystem>("vk");
  factory.Add<gpu::null::NullGraphicsSystem>("null");
  return factory.Create(cvars::gpu);
}

std::vector<std::unique_ptr<hid::InputDriver>> CreateInputDrivers(
    ui::Window* window) {
  std::vector<std::unique_ptr<hid::InputDriver>> drivers;
  if (cvars::hid.compare("nop") == 0) {
    drivers.emplace_back(xe::hid::nop::Create(window));
  } else {
    Factory<hid::InputDriver, ui::Window*> factory;
#if XE_PLATFORM_WIN32
    factory.Add("xinput", xe::hid::xinput::Create);
    // WinKey input driver should always be the last input driver added!
    factory.Add("winkey", xe::hid::winkey::Create);
#endif  // XE_PLATFORM_WIN32
    factory.Add("sdl", xe::hid::sdl::Create);
    for (auto& driver : factory.CreateAll(cvars::hid, window)) {
      if (XSUCCEEDED(driver->Setup())) {
        drivers.emplace_back(std::move(driver));
      }
    }
    if (drivers.empty()) {
      // Fallback to nop if none created.
      drivers.emplace_back(xe::hid::nop::Create(window));
    }
  }
  return drivers;
}

int xenia_main(const std::vector<std::wstring>& args) {
  Profiler::Initialize();
  Profiler::ThreadEnter("main");

  // Figure out where content should go.
  std::wstring content_root = xe::to_wstring(cvars::content_root);
  std::wstring config_folder;

  if (content_root.empty()) {
    auto base_path = xe::filesystem::GetExecutableFolder();
    base_path = xe::to_absolute_path(base_path);

    auto portable_path = xe::join_paths(base_path, L"portable.txt");
    if (xe::filesystem::PathExists(portable_path)) {
      content_root = xe::join_paths(base_path, L"content");
      config_folder = base_path;
    } else {
      content_root = xe::filesystem::GetUserFolder();
#if defined(XE_PLATFORM_WIN32)
      content_root = xe::join_paths(content_root, L"Xenia");
#elif defined(XE_PLATFORM_LINUX)
      content_root = xe::join_paths(content_root, L"Xenia");
#else
#warning Unhandled platform for content root.
      content_root = xe::join_paths(content_root, L"Xenia");
#endif
      config_folder = content_root;
      content_root = xe::join_paths(content_root, L"content");
    }
  }
  content_root = xe::to_absolute_path(content_root);

  XELOGI("Content root: %S", content_root.c_str());
  config::SetupConfig(config_folder);

  if (cvars::discord) {
    discord::DiscordPresence::Initialize();
    discord::DiscordPresence::NotPlaying();
  }

  // Create the emulator but don't initialize so we can setup the window.
  auto emulator = std::make_unique<Emulator>(L"", content_root);

  // Main emulator display window.
  auto emulator_window = EmulatorWindow::Create(emulator.get());

  // Setup and initialize all subsystems. If we can't do something
  // (unsupported system, memory issues, etc) this will fail early.
  X_STATUS result =
      emulator->Setup(emulator_window->window(), CreateAudioSystem,
                      CreateGraphicsSystem, CreateInputDrivers);
  if (XFAILED(result)) {
    XELOGE("Failed to setup emulator: %.8X", result);
    return 1;
  }

  if (cvars::mount_scratch) {
    auto scratch_device = std::make_unique<xe::vfs::HostPathDevice>(
        "\\SCRATCH", L"scratch", false);
    if (!scratch_device->Initialize()) {
      XELOGE("Unable to scan scratch path");
    } else {
      if (!emulator->file_system()->RegisterDevice(std::move(scratch_device))) {
        XELOGE("Unable to register scratch path");
      } else {
        emulator->file_system()->RegisterSymbolicLink("scratch:", "\\SCRATCH");
      }
    }
  }

  if (cvars::mount_cache) {
    auto cache0_device =
        std::make_unique<xe::vfs::HostPathDevice>("\\CACHE0", L"cache0", false);
    if (!cache0_device->Initialize()) {
      XELOGE("Unable to scan cache0 path");
    } else {
      if (!emulator->file_system()->RegisterDevice(std::move(cache0_device))) {
        XELOGE("Unable to register cache0 path");
      } else {
        emulator->file_system()->RegisterSymbolicLink("cache0:", "\\CACHE0");
      }
    }

    auto cache1_device =
        std::make_unique<xe::vfs::HostPathDevice>("\\CACHE1", L"cache1", false);
    if (!cache1_device->Initialize()) {
      XELOGE("Unable to scan cache1 path");
    } else {
      if (!emulator->file_system()->RegisterDevice(std::move(cache1_device))) {
        XELOGE("Unable to register cache1 path");
      } else {
        emulator->file_system()->RegisterSymbolicLink("cache1:", "\\CACHE1");
      }
    }
  }

  // Set a debug handler.
  // This will respond to debugging requests so we can open the debug UI.
  std::unique_ptr<xe::debug::ui::DebugWindow> debug_window;
  if (cvars::debug) {
    emulator->processor()->set_debug_listener_request_handler(
        [&](xe::cpu::Processor* processor) {
          if (debug_window) {
            return debug_window.get();
          }
          emulator_window->loop()->PostSynchronous([&]() {
            debug_window = xe::debug::ui::DebugWindow::Create(
                emulator.get(), emulator_window->loop());
            debug_window->window()->on_closed.AddListener(
                [&](xe::ui::UIEvent* e) {
                  emulator->processor()->set_debug_listener(nullptr);
                  emulator_window->loop()->Post(
                      [&]() { debug_window.reset(); });
                });
          });
          return debug_window.get();
        });
  }

  auto evt = xe::threading::Event::CreateAutoResetEvent(false);
  emulator->on_launch.AddListener([&](auto title_id, const auto& game_title) {
    if (cvars::discord) {
      discord::DiscordPresence::PlayingTitle(
          game_title.empty() ? L"Unknown Title" : game_title);
    }
    emulator_window->UpdateTitle();
    evt->Set();
  });

  emulator->on_terminate.AddListener([&]() {
    if (cvars::discord) {
      discord::DiscordPresence::NotPlaying();
    }
  });

  emulator_window->window()->on_closing.AddListener([&](ui::UIEvent* e) {
    // This needs to shut down before the graphics context.
    Profiler::Shutdown();
  });

  bool exiting = false;
  emulator_window->loop()->on_quit.AddListener([&](ui::UIEvent* e) {
    exiting = true;
    evt->Set();

    if (cvars::discord) {
      discord::DiscordPresence::Shutdown();
    }

    // TODO(DrChat): Remove this code and do a proper exit.
    XELOGI("Cheap-skate exit!");
    exit(0);
  });

  // Enable the main menu now that the emulator is properly loaded
  emulator_window->window()->EnableMainMenu();

  // Grab path from the flag or unnamed argument.
  std::wstring path;
  if (!cvars::target.empty()) {
    path = xe::to_wstring(cvars::target);
  }

  // Toggles fullscreen
  if (cvars::fullscreen) emulator_window->ToggleFullscreen();

  if (!path.empty()) {
    // Normalize the path and make absolute.
    std::wstring abs_path = xe::to_absolute_path(path);
    result = emulator->LaunchPath(abs_path);
    if (XFAILED(result)) {
      xe::FatalError("Failed to launch target: %.8X", result);
      emulator.reset();
      emulator_window.reset();
      return 1;
    }
  }

  // Now, we're going to use the main thread to drive events related to
  // emulation.
  while (!exiting) {
    xe::threading::Wait(evt.get(), false);

    while (true) {
      emulator->WaitUntilExit();
      if (emulator->TitleRequested()) {
        emulator->LaunchNextTitle();
      } else {
        break;
      }
    }
  }

  debug_window.reset();
  emulator.reset();

  if (cvars::discord) {
    discord::DiscordPresence::Shutdown();
  }

  Profiler::Dump();
  Profiler::Shutdown();
  emulator_window.reset();
  return 0;
}

}  // namespace app
}  // namespace xe

DEFINE_ENTRY_POINT(L"xenia", xe::app::xenia_main, "[Path to .iso/.xex]",
                   "target");
<|endoftext|>


<commit_before>/**
 * This file is part of Slideshow.
 * Copyright (C) 2008-2010 David Sveningsson <ext@sidvind.com>
 *                         Pernilla Sveningsson <estel@sidvind.com>
 *
 * Slideshow is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * Slideshow is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Slideshow.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#	include "config.h"
#endif

#include "backend/SDLbackend.h"
#include "exception.h"

#ifdef WIN32
#	include "win32.h"
#endif

static const int sdl_flags = SDL_OPENGL | SDL_DOUBLEBUF;
static PlatformBackend* factory(void){
	return new SDLBackend;
}

void SDLBackend::register_factory(){
	PlatformBackend::register_factory("sdl", ::factory);
}

SDLBackend::SDLBackend()
	: PlatformBackend()
	, _lock(false) {

}

SDLBackend::~SDLBackend(){

}

int SDLBackend::init(const Vector2ui &resolution, bool fullscreen){
	set_resolution(resolution.width, resolution.height);

	int flags = sdl_flags;

	if ( fullscreen ){
		flags |= SDL_FULLSCREEN;
	}

	_fullscreen = fullscreen;

	if ( SDL_Init(SDL_INIT_VIDEO) < 0 ){
		throw exception("Unable to init SDL: %s", SDL_GetError());
	}

	if ( SDL_SetVideoMode(resolution.width, resolution.height, 0, flags) == NULL ){
		throw exception("Unable to init SDL: %s", SDL_GetError());
	}

	SDL_EnableUNICODE(1);
	SDL_ShowCursor(SDL_DISABLE);

#ifdef WIN32
	SetConsoleOutputCP(65001);
#endif

	return 0;
}

void SDLBackend::cleanup(){
	SDL_Quit();
}

void SDLBackend::poll(bool& running){
	SDL_Event event;
	while(SDL_PollEvent(&event) ){
		switch(event.type){
		case SDL_KEYDOWN:
			if ( event.key.keysym.sym == SDLK_ESCAPE ){
				running = false;
			}

			if ( (event.key.keysym.sym == SDLK_RETURN) && (event.key.keysym.mod & KMOD_ALT)){
				_fullscreen = !_fullscreen;
				int flags = sdl_flags;

				if ( _fullscreen ){
					flags |= SDL_FULLSCREEN;
				}

				SDL_SetVideoMode(resolution().width, resolution().height, 0, flags);
			}

			break;

		case SDL_VIDEORESIZE:
			printf("video resize\n");
			set_resolution(event.resize.w, event.resize.h);
			break;

		case SDL_QUIT:
			running = false;
			break;
		}
	}
}

void SDLBackend::swap_buffers() const {
	SDL_GL_SwapBuffers();
}

void SDLBackend::lock_mouse(bool state){
	_lock = state;

	if ( _lock ){
		/* casting to Uint16 to silence warning */
		SDL_WarpMouse((Uint16)center().x, (Uint16)center().y);
	}
}
<commit_msg>removing debug print<commit_after>/**
 * This file is part of Slideshow.
 * Copyright (C) 2008-2010 David Sveningsson <ext@sidvind.com>
 *                         Pernilla Sveningsson <estel@sidvind.com>
 *
 * Slideshow is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * Slideshow is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Slideshow.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#	include "config.h"
#endif

#include "backend/SDLbackend.h"
#include "exception.h"

#ifdef WIN32
#	include "win32.h"
#endif

static const int sdl_flags = SDL_OPENGL | SDL_DOUBLEBUF;
static PlatformBackend* factory(void){
	return new SDLBackend;
}

void SDLBackend::register_factory(){
	PlatformBackend::register_factory("sdl", ::factory);
}

SDLBackend::SDLBackend()
	: PlatformBackend()
	, _lock(false) {

}

SDLBackend::~SDLBackend(){

}

int SDLBackend::init(const Vector2ui &resolution, bool fullscreen){
	set_resolution(resolution.width, resolution.height);

	int flags = sdl_flags;

	if ( fullscreen ){
		flags |= SDL_FULLSCREEN;
	}

	_fullscreen = fullscreen;

	if ( SDL_Init(SDL_INIT_VIDEO) < 0 ){
		throw exception("Unable to init SDL: %s", SDL_GetError());
	}

	if ( SDL_SetVideoMode(resolution.width, resolution.height, 0, flags) == NULL ){
		throw exception("Unable to init SDL: %s", SDL_GetError());
	}

	SDL_EnableUNICODE(1);
	SDL_ShowCursor(SDL_DISABLE);

#ifdef WIN32
	SetConsoleOutputCP(65001);
#endif

	return 0;
}

void SDLBackend::cleanup(){
	SDL_Quit();
}

void SDLBackend::poll(bool& running){
	SDL_Event event;
	while(SDL_PollEvent(&event) ){
		switch(event.type){
		case SDL_KEYDOWN:
			if ( event.key.keysym.sym == SDLK_ESCAPE ){
				running = false;
			}

			if ( (event.key.keysym.sym == SDLK_RETURN) && (event.key.keysym.mod & KMOD_ALT)){
				_fullscreen = !_fullscreen;
				int flags = sdl_flags;

				if ( _fullscreen ){
					flags |= SDL_FULLSCREEN;
				}

				SDL_SetVideoMode(resolution().width, resolution().height, 0, flags);
			}

			break;

		case SDL_VIDEORESIZE:
			set_resolution(event.resize.w, event.resize.h);
			break;

		case SDL_QUIT:
			running = false;
			break;
		}
	}
}

void SDLBackend::swap_buffers() const {
	SDL_GL_SwapBuffers();
}

void SDLBackend::lock_mouse(bool state){
	_lock = state;

	if ( _lock ){
		/* casting to Uint16 to silence warning */
		SDL_WarpMouse((Uint16)center().x, (Uint16)center().y);
	}
}
<|endoftext|>


<commit_before>/*
* Policies for TLS
* (C) 2004-2010,2012 Jack Lloyd
*
* Released under the terms of the Botan license
*/

#include <botan/tls_policy.h>
#include <botan/tls_suites.h>
#include <botan/tls_exceptn.h>
#include <botan/internal/stl_util.h>

namespace Botan {

namespace TLS {

std::vector<std::string> Policy::allowed_ciphers() const
   {
   std::vector<std::string> allowed;
   allowed.push_back("AES-256");
   allowed.push_back("AES-128");
   allowed.push_back("TripleDES");
   allowed.push_back("ARC4");
   // Note that SEED is not included by default
   return allowed;
   }

std::vector<std::string> Policy::allowed_hashes() const
   {
   std::vector<std::string> allowed;
   allowed.push_back("SHA-512");
   allowed.push_back("SHA-384");
   allowed.push_back("SHA-256");
   allowed.push_back("SHA-224");
   allowed.push_back("SHA-1");
   // Note that MD5 is not included by default
   return allowed;
   }

std::vector<std::string> Policy::allowed_key_exchange_methods() const
   {
   std::vector<std::string> allowed;
   //allowed.push_back("SRP");
   //allowed.push_back("ECDH");
   allowed.push_back("DH");
   allowed.push_back(""); // means RSA via server cert
   return allowed;
   }

std::vector<std::string> Policy::allowed_signature_methods() const
   {
   std::vector<std::string> allowed;
   //allowed.push_back("ECDSA");
   allowed.push_back("RSA");
   allowed.push_back("DSA");
   return allowed;
   }

namespace {

class Ciphersuite_Preference_Ordering
   {
   public:
      Ciphersuite_Preference_Ordering(const std::vector<std::string>& ciphers,
                                      const std::vector<std::string>& hashes,
                                      const std::vector<std::string>& kex,
                                      const std::vector<std::string>& sigs) :
         m_ciphers(ciphers), m_hashes(hashes), m_kex(kex), m_sigs(sigs) {}

      bool operator()(const Ciphersuite& a, const Ciphersuite& b) const
         {
         if(a.kex_algo() != b.kex_algo())
            {
            for(size_t i = 0; i != m_kex.size(); ++i)
               {
               if(a.kex_algo() == m_kex[i])
                  return true;
               if(b.kex_algo() == m_kex[i])
                  return false;
               }
            }

         if(a.cipher_algo() != b.cipher_algo())
            {
            for(size_t i = 0; i != m_ciphers.size(); ++i)
               {
               if(a.cipher_algo() == m_ciphers[i])
                  return true;
               if(b.cipher_algo() == m_ciphers[i])
                  return false;
               }
            }

         if(a.sig_algo() != b.sig_algo())
            {
            for(size_t i = 0; i != m_sigs.size(); ++i)
               {
               if(a.sig_algo() == m_sigs[i])
                  return true;
               if(b.sig_algo() == m_sigs[i])
                  return false;
               }
            }

         if(a.mac_algo() != b.mac_algo())
            {
            for(size_t i = 0; i != m_hashes.size(); ++i)
               {
               if(a.mac_algo() == m_hashes[i])
                  return true;
               if(b.mac_algo() == m_hashes[i])
                  return false;
               }
            }

         return false; // equal (?!?)
         }
   private:
      std::vector<std::string> m_ciphers, m_hashes, m_kex, m_sigs;

   };

}

std::vector<u16bit> Policy::ciphersuite_list(bool have_srp) const
   {
   std::vector<std::string> ciphers = allowed_ciphers();
   std::vector<std::string> hashes = allowed_hashes();
   std::vector<std::string> kex = allowed_key_exchange_methods();
   std::vector<std::string> sigs = allowed_signature_methods();

   if(!have_srp)
      {
      std::vector<std::string>::iterator i = std::find(kex.begin(), kex.end(), "SRP");

      if(i != kex.end())
         kex.erase(i);
      }

   Ciphersuite_Preference_Ordering order(ciphers, hashes, kex, sigs);

   std::map<Ciphersuite, u16bit, Ciphersuite_Preference_Ordering> ciphersuites(order);

   // When in doubt use brute force :)
   for(u32bit i = 0; i != 65536; ++i)
      {
      Ciphersuite suite = Ciphersuite::lookup_ciphersuite(i);
      if(suite.cipher_keylen() == 0)
         continue; // not a ciphersuite we know

      if(value_exists(ciphers, suite.cipher_algo()) &&
         value_exists(hashes, suite.mac_algo()) &&
         value_exists(kex, suite.kex_algo()) &&
         value_exists(sigs, suite.sig_algo()))
         {
         ciphersuites[suite] = i;
         }
      }

   std::vector<u16bit> ciphersuite_codes;

   for(std::map<Ciphersuite, u16bit, Ciphersuite_Preference_Ordering>::iterator i = ciphersuites.begin();
       i != ciphersuites.end(); ++i)
      {
      ciphersuite_codes.push_back(i->second);
      }

   return ciphersuite_codes;
   }

/*
* Return allowed compression algorithms
*/
std::vector<byte> Policy::compression() const
   {
   std::vector<byte> algs;
   algs.push_back(NO_COMPRESSION);
   return algs;
   }

/*
* Choose which ciphersuite to use
*/
u16bit Policy::choose_suite(const std::vector<u16bit>& client_suites,
                                bool have_rsa,
                                bool have_dsa,
                                bool have_srp) const
   {
   for(size_t i = 0; i != client_suites.size(); ++i)
      {
      const u16bit suite_id = client_suites[i];
      Ciphersuite suite = Ciphersuite::lookup_ciphersuite(suite_id);

      if(suite.cipher_keylen() == 0)
         continue; // not a ciphersuite we know

      if(suite.kex_algo() == "ECDH")
         continue; // not currently supported

      if(suite.sig_algo() == "RSA" && have_rsa)
         return suite_id;

      if(suite.sig_algo() == "DSA" && have_dsa)
         return suite_id;

      if(suite.kex_algo() == "SRP" && have_srp)
         return suite_id;

#if 0
      if(suite.sig_algo() == "") // anonymous server
         return suite_id;
#endif
      }

   return 0;
   }

/*
* Choose which compression algorithm to use
*/
byte Policy::choose_compression(const std::vector<byte>& c_comp) const
   {
   std::vector<byte> s_comp = compression();

   for(size_t i = 0; i != s_comp.size(); ++i)
      for(size_t j = 0; j != c_comp.size(); ++j)
         if(s_comp[i] == c_comp[j])
            return s_comp[i];

   return NO_COMPRESSION;
   }

}

}
<commit_msg>Allow ECDH negotiation by default<commit_after>/*
* Policies for TLS
* (C) 2004-2010,2012 Jack Lloyd
*
* Released under the terms of the Botan license
*/

#include <botan/tls_policy.h>
#include <botan/tls_suites.h>
#include <botan/tls_exceptn.h>
#include <botan/internal/stl_util.h>

namespace Botan {

namespace TLS {

std::vector<std::string> Policy::allowed_ciphers() const
   {
   std::vector<std::string> allowed;
   allowed.push_back("AES-256");
   allowed.push_back("AES-128");
   allowed.push_back("TripleDES");
   allowed.push_back("ARC4");
   // Note that SEED is not included by default
   return allowed;
   }

std::vector<std::string> Policy::allowed_hashes() const
   {
   std::vector<std::string> allowed;
   allowed.push_back("SHA-512");
   allowed.push_back("SHA-384");
   allowed.push_back("SHA-256");
   allowed.push_back("SHA-224");
   allowed.push_back("SHA-1");
   // Note that MD5 is not included by default
   return allowed;
   }

std::vector<std::string> Policy::allowed_key_exchange_methods() const
   {
   std::vector<std::string> allowed;
   //allowed.push_back("SRP");
   allowed.push_back("ECDH");
   allowed.push_back("DH");
   allowed.push_back(""); // means RSA via server cert
   return allowed;
   }

std::vector<std::string> Policy::allowed_signature_methods() const
   {
   std::vector<std::string> allowed;
   //allowed.push_back("ECDSA");
   allowed.push_back("RSA");
   allowed.push_back("DSA");
   return allowed;
   }

namespace {

class Ciphersuite_Preference_Ordering
   {
   public:
      Ciphersuite_Preference_Ordering(const std::vector<std::string>& ciphers,
                                      const std::vector<std::string>& hashes,
                                      const std::vector<std::string>& kex,
                                      const std::vector<std::string>& sigs) :
         m_ciphers(ciphers), m_hashes(hashes), m_kex(kex), m_sigs(sigs) {}

      bool operator()(const Ciphersuite& a, const Ciphersuite& b) const
         {
         if(a.kex_algo() != b.kex_algo())
            {
            for(size_t i = 0; i != m_kex.size(); ++i)
               {
               if(a.kex_algo() == m_kex[i])
                  return true;
               if(b.kex_algo() == m_kex[i])
                  return false;
               }
            }

         if(a.cipher_algo() != b.cipher_algo())
            {
            for(size_t i = 0; i != m_ciphers.size(); ++i)
               {
               if(a.cipher_algo() == m_ciphers[i])
                  return true;
               if(b.cipher_algo() == m_ciphers[i])
                  return false;
               }
            }

         if(a.sig_algo() != b.sig_algo())
            {
            for(size_t i = 0; i != m_sigs.size(); ++i)
               {
               if(a.sig_algo() == m_sigs[i])
                  return true;
               if(b.sig_algo() == m_sigs[i])
                  return false;
               }
            }

         if(a.mac_algo() != b.mac_algo())
            {
            for(size_t i = 0; i != m_hashes.size(); ++i)
               {
               if(a.mac_algo() == m_hashes[i])
                  return true;
               if(b.mac_algo() == m_hashes[i])
                  return false;
               }
            }

         return false; // equal (?!?)
         }
   private:
      std::vector<std::string> m_ciphers, m_hashes, m_kex, m_sigs;

   };

}

std::vector<u16bit> Policy::ciphersuite_list(bool have_srp) const
   {
   std::vector<std::string> ciphers = allowed_ciphers();
   std::vector<std::string> hashes = allowed_hashes();
   std::vector<std::string> kex = allowed_key_exchange_methods();
   std::vector<std::string> sigs = allowed_signature_methods();

   if(!have_srp)
      {
      std::vector<std::string>::iterator i = std::find(kex.begin(), kex.end(), "SRP");

      if(i != kex.end())
         kex.erase(i);
      }

   Ciphersuite_Preference_Ordering order(ciphers, hashes, kex, sigs);

   std::map<Ciphersuite, u16bit, Ciphersuite_Preference_Ordering> ciphersuites(order);

   // When in doubt use brute force :)
   for(u32bit i = 0; i != 65536; ++i)
      {
      Ciphersuite suite = Ciphersuite::lookup_ciphersuite(i);
      if(suite.cipher_keylen() == 0)
         continue; // not a ciphersuite we know

      if(value_exists(ciphers, suite.cipher_algo()) &&
         value_exists(hashes, suite.mac_algo()) &&
         value_exists(kex, suite.kex_algo()) &&
         value_exists(sigs, suite.sig_algo()))
         {
         ciphersuites[suite] = i;
         }
      }

   std::vector<u16bit> ciphersuite_codes;

   for(std::map<Ciphersuite, u16bit, Ciphersuite_Preference_Ordering>::iterator i = ciphersuites.begin();
       i != ciphersuites.end(); ++i)
      {
      ciphersuite_codes.push_back(i->second);
      }

   return ciphersuite_codes;
   }

/*
* Return allowed compression algorithms
*/
std::vector<byte> Policy::compression() const
   {
   std::vector<byte> algs;
   algs.push_back(NO_COMPRESSION);
   return algs;
   }

/*
* Choose which ciphersuite to use
*/
u16bit Policy::choose_suite(const std::vector<u16bit>& client_suites,
                                bool have_rsa,
                                bool have_dsa,
                                bool have_srp) const
   {
   for(size_t i = 0; i != client_suites.size(); ++i)
      {
      const u16bit suite_id = client_suites[i];
      Ciphersuite suite = Ciphersuite::lookup_ciphersuite(suite_id);

      if(suite.cipher_keylen() == 0)
         continue; // not a ciphersuite we know

      if(suite.sig_algo() == "RSA" && have_rsa)
         return suite_id;

      if(suite.sig_algo() == "DSA" && have_dsa)
         return suite_id;

      if(suite.kex_algo() == "SRP" && have_srp)
         return suite_id;

#if 0
      if(suite.sig_algo() == "") // anonymous server
         return suite_id;
#endif
      }

   return 0;
   }

/*
* Choose which compression algorithm to use
*/
byte Policy::choose_compression(const std::vector<byte>& c_comp) const
   {
   std::vector<byte> s_comp = compression();

   for(size_t i = 0; i != s_comp.size(); ++i)
      for(size_t j = 0; j != c_comp.size(); ++j)
         if(s_comp[i] == c_comp[j])
            return s_comp[i];

   return NO_COMPRESSION;
   }

}

}
<|endoftext|>


<commit_before>#include <gflags/gflags.h>
#include <stddef.h>
#include <chrono>
#include <iostream>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>

#include "ncode/common.h"
#include "ncode/file.h"
#include "ncode/logging.h"
#include "ncode/strutil.h"
#include "ncode/lp/demand_matrix.h"
#include "ncode/net/net_common.h"
#include "ncode/net/net_gen.h"
#include "ncode/thread_runner.h"
#include "pcap_data.h"

using namespace std::chrono;

DEFINE_string(topology, "", "The topology");
DEFINE_string(traffic_matrix, "", "A file with a traffic matrix");
DEFINE_string(pcap_trace_store, "trace_store.pb",
              "A file with information about .pcap traces");
DEFINE_string(out, "",
              "Will output a series of PBTraceToFitRate protobufs, they will "
              "only be compatible with the same trace_store.pb");
DEFINE_uint64(period_duration_ms, 60000, "Length of the period");
DEFINE_double(tm_scale, 1.0, "By how much to scale the traffic matrix");
DEFINE_uint64(seed, 1, "Seed for the RNG");
DEFINE_uint64(passes, 10, "Number of passes to perform for each rate");
DEFINE_string(to_exclude,
              "equinix-sanjose_A_12_19_2013,equinix-chicago_A_11_21_2013",
              "Traces to exclude");
DEFINE_double(thin_fraction, 0.0, "Fraction of aggregates to thin");
DEFINE_double(
    thin_magnitude, 0.1,
    "What fraction of slices to keep for aggregates that are thinned");

using Input = std::tuple<nc::net::Bandwidth, size_t, ctr::PcapDataBinCache*,
                         const ctr::BinSequenceGenerator*>;

static std::unique_ptr<ctr::BinSequence> HandleMatrixElement(
    const Input& input) {
  nc::net::Bandwidth demand;
  size_t i;
  ctr::PcapDataBinCache* bin_cache;
  const ctr::BinSequenceGenerator* sequence_generator;
  std::tie(demand, i, bin_cache, sequence_generator) = input;

  // The RNG.
  std::mt19937 rnd(FLAGS_seed + i);

  std::unique_ptr<ctr::BinSequence> bin_sequence = sequence_generator->Next(
      demand, milliseconds(FLAGS_period_duration_ms), bin_cache, &rnd);

  LOG(INFO) << "Looking for traces to match " << demand.Mbps() << "Mbps got "
            << bin_sequence->MeanRate(bin_cache).Mbps();

  CHECK(bin_sequence);
  return bin_sequence;
}

static std::string StripExtension(const std::string& filename) {
  std::size_t found = filename.find_last_of(".");
  return filename.substr(0, found);
}

static std::unique_ptr<ctr::BinSequence> ThinSequence(
    const ctr::BinSequence& bin_sequence, ctr::PcapDataBinCache* cache) {
  nc::net::Bandwidth target_rate = bin_sequence.MeanRate(cache);
  nc::net::Bandwidth max_rate = bin_sequence.MaxRate(cache);

  auto thinned = bin_sequence.Thin(FLAGS_thin_fraction);
  nc::net::Bandwidth bw = thinned->MeanRate(cache);
  double scale = target_rate / bw;
  auto out = std::move(thinned->PreciseSplitOrDie({scale})[0]);

  nc::net::Bandwidth rate_after_thinning = out->MeanRate(cache);
  nc::net::Bandwidth max_rate_after_thinning = out->MaxRate(cache);

  if (rate_after_thinning == nc::net::Bandwidth::Zero()) {
    return bin_sequence.Duplicate();
  }

  LOG(INFO) << "Will thin aggregate with rate " << target_rate.Mbps() << " max "
            << max_rate.Mbps() << " to rate " << rate_after_thinning.Mbps()
            << " max " << max_rate_after_thinning.Mbps();
  return out;
}

int main(int argc, char** argv) {
  gflags::ParseCommandLineFlags(&argc, &argv, true);

  CHECK(!FLAGS_topology.empty());
  CHECK(!FLAGS_traffic_matrix.empty());

  std::string out = FLAGS_out;
  if (out.empty()) {
    out = nc::StrCat(StripExtension(FLAGS_traffic_matrix), "_fit.pb");
  }

  if (nc::File::Exists(out)) {
    LOG(ERROR) << "Output " << out << " already exists";
    return 0;
  }

  std::vector<std::string> node_order;
  nc::net::GraphBuilder builder = nc::net::LoadRepetitaOrDie(
      nc::File::ReadFileToStringOrDie(FLAGS_topology), &node_order);
  builder.RemoveMultipleLinks();
  nc::net::GraphStorage graph(builder);

  std::vector<std::string> to_exclude_v = nc::Split(FLAGS_to_exclude, ",");
  std::set<std::string> to_exclude(to_exclude_v.begin(), to_exclude_v.end());
  auto filter = ctr::PcapTraceStore::FilterFunction(
      [&to_exclude](const ctr::TraceId& trace_id) {
        if (nc::ContainsKey(to_exclude, trace_id.ToString())) {
          LOG(INFO) << "Filtering " << trace_id.ToString();
          return true;
        }

        return false;
      });

  ctr::PcapTraceStore trace_store(FLAGS_pcap_trace_store);
  ctr::BinSequenceGenerator bin_sequence_generator(
      trace_store.AllTracesExcept(filter), {milliseconds(0)});

  std::unique_ptr<nc::lp::DemandMatrix> demand_matrix =
      nc::lp::DemandMatrix::LoadRepetitaFileOrDie(FLAGS_traffic_matrix,
                                                  node_order, &graph);
  demand_matrix = demand_matrix->Scale(FLAGS_tm_scale);

  // The bin cache.
  ctr::PcapDataBinCache bin_cache;

  std::vector<Input> inputs;
  for (size_t i = 0; i < demand_matrix->elements().size(); ++i) {
    nc::net::Bandwidth demand = demand_matrix->elements()[i].demand;
    inputs.emplace_back(demand, i, &bin_cache, &bin_sequence_generator);
  }

  // It is tricky to get good-performance thread-safe code in pcap_data.cc
  // because of the copies the cache would have to do. So for now we only do one
  // thread.
  std::vector<std::unique_ptr<ctr::BinSequence>> results =
      nc::RunInParallelWithResult<Input, ctr::BinSequence>(
          inputs, [](const Input& input) { return HandleMatrixElement(input); },
          1);

  // Will now thin aggregates if needed.
  std::mt19937 rnd(FLAGS_seed);
  std::shuffle(results.begin(), results.end(), rnd);
  CHECK(FLAGS_thin_fraction <= 1.0);
  CHECK(FLAGS_thin_fraction >= 0.0);

  size_t thin_limit = FLAGS_thin_fraction * results.size();
  for (size_t i = 0; i <= thin_limit; ++i) {
    results[i] = ThinSequence(*results[i], &bin_cache);
  }

  for (size_t i = 0; i < demand_matrix->elements().size(); ++i) {
    ctr::PcapTraceFitStore::AddToStore(*results[i], out, &bin_cache);
  }

  return 0;
}
<commit_msg>updates to pcap_data<commit_after>#include <gflags/gflags.h>
#include <stddef.h>
#include <chrono>
#include <iostream>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>

#include "ncode/common.h"
#include "ncode/file.h"
#include "ncode/logging.h"
#include "ncode/strutil.h"
#include "ncode/lp/demand_matrix.h"
#include "ncode/net/net_common.h"
#include "ncode/net/net_gen.h"
#include "ncode/thread_runner.h"
#include "pcap_data.h"

using namespace std::chrono;

DEFINE_string(topology, "", "The topology");
DEFINE_string(traffic_matrix, "", "A file with a traffic matrix");
DEFINE_string(pcap_trace_store, "trace_store.pb",
              "A file with information about .pcap traces");
DEFINE_string(out, "",
              "Will output a series of PBTraceToFitRate protobufs, they will "
              "only be compatible with the same trace_store.pb");
DEFINE_uint64(period_duration_ms, 60000, "Length of the period");
DEFINE_double(tm_scale, 1.0, "By how much to scale the traffic matrix");
DEFINE_uint64(seed, 1, "Seed for the RNG");
DEFINE_uint64(passes, 10, "Number of passes to perform for each rate");
DEFINE_string(to_exclude,
              "equinix-sanjose_A_12_19_2013,equinix-chicago_A_11_21_2013",
              "Traces to exclude");
DEFINE_double(thin_fraction, 0.0, "Fraction of aggregates to thin");
DEFINE_double(
    thin_magnitude, 0.1,
    "What fraction of slices to keep for aggregates that are thinned");

using Input = std::tuple<nc::net::Bandwidth, size_t, ctr::PcapDataBinCache*,
                         const ctr::BinSequenceGenerator*>;

static std::unique_ptr<ctr::BinSequence> HandleMatrixElement(
    const Input& input) {
  nc::net::Bandwidth demand;
  size_t i;
  ctr::PcapDataBinCache* bin_cache;
  const ctr::BinSequenceGenerator* sequence_generator;
  std::tie(demand, i, bin_cache, sequence_generator) = input;

  // The RNG.
  std::mt19937 rnd(FLAGS_seed + i);

  std::unique_ptr<ctr::BinSequence> bin_sequence = sequence_generator->Next(
      demand, milliseconds(FLAGS_period_duration_ms), bin_cache, &rnd);

  LOG(INFO) << "Looking for traces to match " << demand.Mbps() << "Mbps got "
            << bin_sequence->MeanRate(bin_cache).Mbps();

  CHECK(bin_sequence);
  return bin_sequence;
}

static std::string StripExtension(const std::string& filename) {
  std::size_t found = filename.find_last_of(".");
  return filename.substr(0, found);
}

static std::unique_ptr<ctr::BinSequence> ThinSequence(
    const ctr::BinSequence& bin_sequence, ctr::PcapDataBinCache* cache) {
  nc::net::Bandwidth target_rate = bin_sequence.MeanRate(cache);
  nc::net::Bandwidth max_rate = bin_sequence.MaxRate(cache);

  auto thinned = bin_sequence.Thin(FLAGS_thin_fraction);
  nc::net::Bandwidth bw = thinned->MeanRate(cache);
  double scale = target_rate / bw;
  auto out = std::move(thinned->PreciseSplitOrDie({scale})[0]);

  nc::net::Bandwidth rate_after_thinning = out->MeanRate(cache);
  nc::net::Bandwidth max_rate_after_thinning = out->MaxRate(cache);

  if (rate_after_thinning == nc::net::Bandwidth::Zero()) {
    return bin_sequence.Duplicate();
  }

  LOG(INFO) << "Will thin aggregate with rate " << target_rate.Mbps() << " max "
            << max_rate.Mbps() << " to rate " << rate_after_thinning.Mbps()
            << " max " << max_rate_after_thinning.Mbps();
  return out;
}

int main(int argc, char** argv) {
  gflags::ParseCommandLineFlags(&argc, &argv, true);

  CHECK(!FLAGS_topology.empty());
  CHECK(!FLAGS_traffic_matrix.empty());

  std::string out = FLAGS_out;
  if (out.empty()) {
    out = nc::StrCat(StripExtension(FLAGS_traffic_matrix), "_fit.pb");
  }

  if (nc::File::Exists(out)) {
    LOG(ERROR) << "Output " << out << " already exists";
    return 0;
  }

  std::vector<std::string> node_order;
  nc::net::GraphBuilder builder = nc::net::LoadRepetitaOrDie(
      nc::File::ReadFileToStringOrDie(FLAGS_topology), &node_order);
  builder.RemoveMultipleLinks();
  nc::net::GraphStorage graph(builder);

  std::vector<std::string> to_exclude_v = nc::Split(FLAGS_to_exclude, ",");
  std::set<std::string> to_exclude(to_exclude_v.begin(), to_exclude_v.end());
  auto filter = ctr::PcapTraceStore::FilterFunction(
      [&to_exclude](const ctr::TraceId& trace_id) {
        if (nc::ContainsKey(to_exclude, trace_id.ToString())) {
          LOG(INFO) << "Filtering " << trace_id.ToString();
          return true;
        }

        return false;
      });

  ctr::PcapTraceStore trace_store(FLAGS_pcap_trace_store);
  ctr::BinSequenceGenerator bin_sequence_generator(
      trace_store.AllTracesExcept(filter), {milliseconds(0)});

  std::unique_ptr<nc::lp::DemandMatrix> demand_matrix =
      nc::lp::DemandMatrix::LoadRepetitaFileOrDie(FLAGS_traffic_matrix,
                                                  node_order, &graph);
  demand_matrix = demand_matrix->Scale(FLAGS_tm_scale);

  // The bin cache.
  ctr::PcapDataBinCache bin_cache;

  std::vector<Input> inputs;
  for (size_t i = 0; i < demand_matrix->elements().size(); ++i) {
    nc::net::Bandwidth demand = demand_matrix->elements()[i].demand;
    inputs.emplace_back(demand, i, &bin_cache, &bin_sequence_generator);
  }

  // It is tricky to get good-performance thread-safe code in pcap_data.cc
  // because of the copies the cache would have to do. So for now we only do one
  // thread.
  std::vector<std::unique_ptr<ctr::BinSequence>> results =
      nc::RunInParallelWithResult<Input, ctr::BinSequence>(
          inputs, [](const Input& input) { return HandleMatrixElement(input); },
          1);

  // Will now thin aggregates if needed.
  std::mt19937 rnd(FLAGS_seed);
  std::shuffle(results.begin(), results.end(), rnd);
  CHECK(FLAGS_thin_fraction <= 1.0);
  CHECK(FLAGS_thin_fraction >= 0.0);

  size_t thin_limit = FLAGS_thin_fraction * results.size();
  for (size_t i = 0; i < thin_limit; ++i) {
    results[i] = ThinSequence(*results[i], &bin_cache);
  }

  for (size_t i = 0; i < demand_matrix->elements().size(); ++i) {
    ctr::PcapTraceFitStore::AddToStore(*results[i], out, &bin_cache);
  }

  return 0;
}
<|endoftext|>