<commit_before>#include "Engine.h"
#include "Game.h"
#include "ObjectDummy.h"
#include "BodyRigid.h"
#include "BodyDummy.h"

#include "Physics.h"
#include "ShapeCapsule.h"
#include "ActorBase.h"
#include "Visualizer.h"
#include "sys/SysControl.h"

#ifdef MEMORY_INFO
#define new new(__FILE__, __LINE__)
#endif // MEMORY_INFO

using namespace MathLib;

/*
*/
#define ACTOR_BASE_IFPS             (1.0f / 60.0f)
#define ACTOR_BASE_CLAMP            89.9f
#define ACTOR_BASE_COLLISIONS       4

/*
*/

CActorBase::CActorBase()
{
	m_vUp = vec3(0.0f, 0.0f, 1.0f);
	m_pObject = new CObjectDummy();
	m_pDummy = new CBodyDummy();
	m_pShape = new CShapeCapsule(1.0f, 1.0f);

	m_nFlush = 0;
	m_vPosition = Vec3_zero;
	m_vVelocity = Vec3_zero;
	m_fPhiAngle = 0.0f;			//б		άƽϵУֱYķX֮ļн
	m_fThetaAngle = 0.0f;			//λǣ뵱ǰ˳ʱ߹ĽǶ

	for (int i = 0; i < NUM_STATES; i++)
	{
		m_pStates[i] = 0;
		m_pTimes[i] = 0.0f;
	}

	m_pDummy->SetEnabled(1);
	m_pObject->SetBody(NULL);
	m_pObject->SetBody(m_pDummy);
	m_pShape->SetBody(NULL);
	m_pShape->SetBody(m_pDummy);

	m_pObject->SetWorldTransform(Get_Body_Transform());
	m_pShape->SetRestitution(0.0f);
	m_pShape->SetCollisionMask(2);

	SetEnabled(1);
	SetViewDirection(vec3(0.0f,1.0f,0.0f));
	SetCollision(1);
	SetCollisionRadius(0.3f);
	SetCollisionHeight(1.0f);
	SetFriction(2.0f);
	SetMinVelocity(2.0f);
	SetMaxVelocity(4.0f);
	SetAcceleration(8.0f);
	SetDamping(8.0f);
	SetJumping(1.5f);

	SetGround(0);
	SetCeiling(0);

}
CActorBase::~CActorBase()
{
	m_pDummy->SetObject(NULL);
	delete m_pObject;
	delete m_pDummy;
}

void CActorBase::SetEnabled(int enable)
{
	m_nEnable = enable;
	m_pDummy->SetEnabled(m_nEnable);
}
int CActorBase::IsEnabled() const
{
	return m_nEnable;
}
void CActorBase::Update(float ifps)
{
	if (!m_nEnable)
	{
		return;
	}
	// impulse
	vec3 impulse = vec3_zero;

	// ortho basis
	vec3 tangent, binormal;
	OrthoBasis(m_vUp, tangent, binormal);
	// current basis
	vec3 x = quat(m_vUp, -m_fPhiAngle) * binormal;
	vec3 y = Normalize(Cross(m_vUp, x));
	vec3 z = Normalize(Cross(x, y));
	// handle states
	Update_States(1, ifps);

	// old velocity
	float x_velocity = Dot(x, m_vVelocity);
	float y_velocity = Dot(y, m_vVelocity);
	float z_velocity = Dot(z, m_vVelocity);

	// movement
	if (m_pStates[STATE_FORWARD]) impulse += x;
	if (m_pStates[STATE_BACKWARD]) impulse -= x;
	if (m_pStates[STATE_MOVE_LEFT]) impulse += y;
	if (m_pStates[STATE_MOVE_RIGHT]) impulse -= y;
	impulse.normalize();
	// velocity
	if (m_pStates[STATE_RUN]) impulse *= m_fMaxVelocity;
	else impulse *= m_fMinVelocity;

	// jump
	if (m_pStates[STATE_JUMP] == STATE_BEGIN)
	{
		impulse += z * CMathCore::Sqrt(2.0f * 9.8f * m_fJumping) / (m_fAcceleration * ifps);
	}

	// rotate velocity
	if (GetGround())
	{
		m_vVelocity = x * x_velocity + y * y_velocity + z * z_velocity;
	}
	// time
	float time = ifps * g_Engine.pPhysics->GetScale();
	// target velocity
	float target_velocity = Length(vec2(Dot(x, impulse), Dot(y, impulse)));

	// penetration tolerance
	float penetration = g_Engine.pPhysics->GetPenetrationTolerance();
	float penetration_2 = penetration * 2.0f;

	// frozen linear velocity
	float frozen_velocity = g_Engine.pPhysics->GetFrozenLinearVelocity();

	// friction
	float friction = 0.0f;
	if (target_velocity < EPSILON)
	{
		friction = m_fFriction;
	}

	// clear collision flags
	if (GetCollision())
	{
		m_nGround = 0;
		m_nCeiling = 0;
	}

	// movement
	do
	{
		// adaptive time step
		float ifps = Min(time, ACTOR_BASE_IFPS);
		time -= ifps;
		// save old velocity
		float old_velocity = Length(vec2(Dot(x, m_vVelocity), Dot(y, m_vVelocity)));

		// integrate velocity
		m_vVelocity += impulse * (m_fAcceleration * ifps);
		m_vVelocity += g_Engine.pPhysics->GetGravity() * ifps;

		// damping
		float current_velocity = Length(vec2(Dot(x, m_vVelocity), Dot(y, m_vVelocity)));
		if (target_velocity < EPSILON || current_velocity > target_velocity)
		{
			m_vVelocity = (x * Dot(x, m_vVelocity) + y * Dot(y, m_vVelocity)) * CMathCore::Exp(-m_fDamping * ifps) + z * Dot(z, m_vVelocity);
		}

		// clamp maximum velocity
		current_velocity = Length(vec2(Dot(x, m_vVelocity), Dot(y, m_vVelocity)));

		if (current_velocity > old_velocity)
		{
			if (current_velocity > target_velocity)
			{
				m_vVelocity = (x * Dot(x, m_vVelocity) + y * Dot(y, m_vVelocity)) * target_velocity / current_velocity + z * Dot(z, m_vVelocity);
			}
		}

	}

}<commit_msg>Signed-off-by: mrlitong <litongtongxue@gmail.com><commit_after>#include "Engine.h"
#include "Game.h"
#include "ObjectDummy.h"
#include "BodyRigid.h"
#include "BodyDummy.h"

#include "Physics.h"
#include "ShapeCapsule.h"
#include "ActorBase.h"
#include "Visualizer.h"
#include "sys/SysControl.h"

#ifdef MEMORY_INFO
#define new new(__FILE__, __LINE__)
#endif // MEMORY_INFO

using namespace MathLib;

/*
*/
#define ACTOR_BASE_IFPS             (1.0f / 60.0f)
#define ACTOR_BASE_CLAMP            89.9f
#define ACTOR_BASE_COLLISIONS       4

/*
*/

CActorBase::CActorBase()
{
	m_vUp = vec3(0.0f, 0.0f, 1.0f);
	m_pObject = new CObjectDummy();
	m_pDummy = new CBodyDummy();
	m_pShape = new CShapeCapsule(1.0f, 1.0f);

	m_nFlush = 0;
	m_vPosition = Vec3_zero;
	m_vVelocity = Vec3_zero;
	m_fPhiAngle = 0.0f;			//б		άƽϵУֱYķX֮ļн
	m_fThetaAngle = 0.0f;			//λǣ뵱ǰ˳ʱ߹ĽǶ

	for (int i = 0; i < NUM_STATES; i++)
	{
		m_pStates[i] = 0;
		m_pTimes[i] = 0.0f;
	}

	m_pDummy->SetEnabled(1);
	m_pObject->SetBody(NULL);
	m_pObject->SetBody(m_pDummy);
	m_pShape->SetBody(NULL);
	m_pShape->SetBody(m_pDummy);

	m_pObject->SetWorldTransform(Get_Body_Transform());
	m_pShape->SetRestitution(0.0f);
	m_pShape->SetCollisionMask(2);

	SetEnabled(1);
	SetViewDirection(vec3(0.0f,1.0f,0.0f));
	SetCollision(1);
	SetCollisionRadius(0.3f);
	SetCollisionHeight(1.0f);
	SetFriction(2.0f);
	SetMinVelocity(2.0f);
	SetMaxVelocity(4.0f);
	SetAcceleration(8.0f);
	SetDamping(8.0f);
	SetJumping(1.5f);

	SetGround(0);
	SetCeiling(0);

}
CActorBase::~CActorBase()
{
	m_pDummy->SetObject(NULL);
	delete m_pObject;
	delete m_pDummy;
}

void CActorBase::SetEnabled(int enable)
{
	m_nEnable = enable;
	m_pDummy->SetEnabled(m_nEnable);
}
int CActorBase::IsEnabled() const
{
	return m_nEnable;
}
void CActorBase::Update(float ifps)
{
	if (!m_nEnable)
	{
		return;
	}
	// impulse
	vec3 impulse = vec3_zero;

	// ortho basis
	vec3 tangent, binormal;
	OrthoBasis(m_vUp, tangent, binormal);
	// current basis
	vec3 x = quat(m_vUp, -m_fPhiAngle) * binormal;
	vec3 y = Normalize(Cross(m_vUp, x));
	vec3 z = Normalize(Cross(x, y));
	// handle states
	Update_States(1, ifps);

	// old velocity
	float x_velocity = Dot(x, m_vVelocity);
	float y_velocity = Dot(y, m_vVelocity);
	float z_velocity = Dot(z, m_vVelocity);

	// movement
	if (m_pStates[STATE_FORWARD]) impulse += x;
	if (m_pStates[STATE_BACKWARD]) impulse -= x;
	if (m_pStates[STATE_MOVE_LEFT]) impulse += y;
	if (m_pStates[STATE_MOVE_RIGHT]) impulse -= y;
	impulse.normalize();
	// velocity
	if (m_pStates[STATE_RUN]) impulse *= m_fMaxVelocity;
	else impulse *= m_fMinVelocity;

	// jump
	if (m_pStates[STATE_JUMP] == STATE_BEGIN)
	{
		impulse += z * CMathCore::Sqrt(2.0f * 9.8f * m_fJumping) / (m_fAcceleration * ifps);
	}

	// rotate velocity
	if (GetGround())
	{
		m_vVelocity = x * x_velocity + y * y_velocity + z * z_velocity;
	}
	// time
	float time = ifps * g_Engine.pPhysics->GetScale();
	// target velocity
	float target_velocity = Length(vec2(Dot(x, impulse), Dot(y, impulse)));

	// penetration tolerance
	float penetration = g_Engine.pPhysics->GetPenetrationTolerance();
	float penetration_2 = penetration * 2.0f;

	// frozen linear velocity
	float frozen_velocity = g_Engine.pPhysics->GetFrozenLinearVelocity();

	// friction
	float friction = 0.0f;
	if (target_velocity < EPSILON)
	{
		friction = m_fFriction;
	}

	// clear collision flags
	if (GetCollision())
	{
		m_nGround = 0;
		m_nCeiling = 0;
	}

	// movement
	do
	{
		// adaptive time step
		float ifps = Min(time, ACTOR_BASE_IFPS);
		time -= ifps;
		// save old velocity
		float old_velocity = Length(vec2(Dot(x, m_vVelocity), Dot(y, m_vVelocity)));

		// integrate velocity
		m_vVelocity += impulse * (m_fAcceleration * ifps);
		m_vVelocity += g_Engine.pPhysics->GetGravity() * ifps;

		// damping
		float current_velocity = Length(vec2(Dot(x, m_vVelocity), Dot(y, m_vVelocity)));
		if (target_velocity < EPSILON || current_velocity > target_velocity)
		{
			m_vVelocity = (x * Dot(x, m_vVelocity) + y * Dot(y, m_vVelocity)) * CMathCore::Exp(-m_fDamping * ifps) + z * Dot(z, m_vVelocity);
		}

		// clamp maximum velocity
		current_velocity = Length(vec2(Dot(x, m_vVelocity), Dot(y, m_vVelocity)));

		if (current_velocity > old_velocity)
		{
			if (current_velocity > target_velocity)
			{
				m_vVelocity = (x * Dot(x, m_vVelocity) + y * Dot(y, m_vVelocity)) * target_velocity / current_velocity + z * Dot(z, m_vVelocity);
			}
		}
		// integrate position
		//m_vPosition += Vec3(m_vVelocity * ifps);

	}

}<|endoftext|>


<commit_before>/**************************************************************************
**
** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "androidrunner.h"

#include "androiddeploystep.h"
#include "androiddeployqtstep.h"
#include "androidconfigurations.h"
#include "androidglobal.h"
#include "androidrunconfiguration.h"
#include "androidmanager.h"

#include <debugger/debuggerrunconfigurationaspect.h>
#include <projectexplorer/target.h>
#include <utils/qtcassert.h>

#include <QTime>
#include <QtConcurrentRun>
#include <QTemporaryFile>
#include <QTcpServer>

namespace Android {
namespace Internal {

typedef QLatin1String _;

AndroidRunner::AndroidRunner(QObject *parent,
                             AndroidRunConfiguration *runConfig,
                             ProjectExplorer::RunMode runMode)
    : QThread(parent)
{
    m_wasStarted = false;
    Debugger::DebuggerRunConfigurationAspect *aspect
            = runConfig->extraAspect<Debugger::DebuggerRunConfigurationAspect>();
    const bool debuggingMode = runMode == ProjectExplorer::DebugRunMode;
    m_useCppDebugger = debuggingMode && aspect->useCppDebugger();
    m_useQmlDebugger = debuggingMode && aspect->useQmlDebugger();
    QString channel = runConfig->remoteChannel();
    QTC_CHECK(channel.startsWith(QLatin1Char(':')));
    m_localGdbServerPort = channel.mid(1).toUShort();
    QTC_CHECK(m_localGdbServerPort);
    m_useQmlProfiler = runMode == ProjectExplorer::QmlProfilerRunMode;
    if (m_useQmlDebugger || m_useQmlProfiler) {
        QTcpServer server;
        QTC_ASSERT(server.listen(QHostAddress::LocalHost)
                   || server.listen(QHostAddress::LocalHostIPv6),
                   qDebug() << tr("No free ports available on host for QML debugging."));
        m_qmlPort = server.serverPort();
    }
    ProjectExplorer::Target *target = runConfig->target();
    m_useLocalQtLibs = AndroidManager::useLocalLibs(target);
    if (m_useLocalQtLibs) {
        int deviceApiLevel = AndroidManager::minimumSDK(target);
        m_localLibs = AndroidManager::loadLocalLibs(target, deviceApiLevel);
        m_localJars = AndroidManager::loadLocalJars(target, deviceApiLevel);
        m_localJarsInitClasses = AndroidManager::loadLocalJarsInitClasses(target, deviceApiLevel);
    }
    m_intentName = AndroidManager::intentName(target);
    m_packageName = m_intentName.left(m_intentName.indexOf(QLatin1Char('/')));

    m_deviceSerialNumber = AndroidManager::deviceSerialNumber(target);
    m_processPID = -1;
    m_adb = AndroidConfigurations::instance().adbToolPath().toString();
    m_selector = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber);

    QString packageDir = _("/data/data/") + m_packageName;
    m_pingFile = packageDir + _("/debug-ping");
    m_pongFile = _("/data/local/tmp/qt/debug-pong-") + m_packageName;
    m_gdbserverSocket = packageDir + _("/debug-socket");
    m_gdbserverPath = packageDir + _("/lib/gdbserver");
    m_gdbserverCommand = m_gdbserverPath + _(" --multi +") + m_gdbserverSocket;
    // Detect busybox, as we need to pass -w to ps to get wide output.
    QProcess psProc;
    psProc.start(m_adb, selector() << _("shell") << _("readlink") << _("$(which ps)"));
    psProc.waitForFinished();
    QByteArray which = psProc.readAll();
    m_isBusyBox = which.startsWith("busybox");

    m_checkPIDTimer.setInterval(1000);

    connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardOutput()), SLOT(logcatReadStandardOutput()));
    connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardError()), SLOT(logcatReadStandardError()));
    connect(&m_checkPIDTimer, SIGNAL(timeout()), SLOT(checkPID()));
}

AndroidRunner::~AndroidRunner()
{
    //stop();
}

static int extractPidFromChunk(const QByteArray &chunk, int from)
{
    int pos1 = chunk.indexOf(' ', from);
    if (pos1 == -1)
        return -1;
    while (chunk[pos1] == ' ')
        ++pos1;
    int pos3 = chunk.indexOf(' ', pos1);
    int pid = chunk.mid(pos1, pos3 - pos1).toInt();
    return pid;
}

static int extractPid(const QString &exeName, const QByteArray &psOutput)
{
    const QByteArray needle = exeName.toUtf8() + '\r';
    const int to = psOutput.indexOf(needle);
    if (to == -1)
        return -1;
    const int from = psOutput.lastIndexOf('\n', to);
    if (from == -1)
        return -1;
    return extractPidFromChunk(psOutput, from);
}

QByteArray AndroidRunner::runPs()
{
    QProcess psProc;
    QStringList args = m_selector;
    args << _("shell") << _("ps");
    if (m_isBusyBox)
        args << _("-w");

    psProc.start(m_adb, args);
    psProc.waitForFinished();
    return psProc.readAll();
}

void AndroidRunner::checkPID()
{
    if (!m_wasStarted)
        return;
    QByteArray psOut = runPs();
    m_processPID = extractPid(m_packageName, psOut);
    if (m_processPID == -1) {
        m_checkPIDTimer.stop();
        emit remoteProcessFinished(tr("\n\n'%1' died.").arg(m_packageName));
    }
}

void AndroidRunner::forceStop()
{
    QProcess proc;
    proc.start(m_adb, selector() << _("shell") << _("am") << _("force-stop"));
    proc.waitForFinished();
}

void AndroidRunner::killPID()
{
    const QByteArray out = runPs();
    int from = 0;
    while (1) {
        const int to = out.indexOf('\n', from);
        if (to == -1)
            break;
        QString line = QString::fromUtf8(out.data() + from, to - from - 1);
        if (line.endsWith(m_packageName) || line.endsWith(m_gdbserverPath)) {
            int pid = extractPidFromChunk(out, from);
            adbKill(pid);
        }
        from = to + 1;
    }
}

void AndroidRunner::start()
{
    m_adbLogcatProcess.start(m_adb, selector() << _("logcat"));
    m_wasStarted = false;
    QtConcurrent::run(this, &AndroidRunner::asyncStart);
}

void AndroidRunner::asyncStart()
{
    QMutexLocker locker(&m_mutex);
    forceStop();
    killPID();

    if (m_useCppDebugger) {
        // Remove pong file.
        QProcess adb;
        adb.start(m_adb, selector() << _("shell") << _("rm") << m_pongFile);
        adb.waitForFinished();
    }

    QStringList args = selector();
    args << _("shell") << _("am") << _("start") << _("-n") << m_intentName;

    if (m_useCppDebugger) {
        QProcess adb;
        adb.start(m_adb, selector() << _("forward")
                  << QString::fromLatin1("tcp:%1").arg(m_localGdbServerPort)
                  << _("localfilesystem:") + m_gdbserverSocket);
        if (!adb.waitForStarted()) {
            emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(adb.errorString()));
            return;
        }
        if (!adb.waitForFinished(-1)) {
            emit remoteProcessFinished(tr("Failed to forward C++ debugging ports."));
            return;
        }

        args << _("-e") << _("debug_ping") << _("true");
        args << _("-e") << _("ping_file") << m_pingFile;
        args << _("-e") << _("pong_file") << m_pongFile;
        args << _("-e") << _("gdbserver_command") << m_gdbserverCommand;
        args << _("-e") << _("gdbserver_socket") << m_gdbserverSocket;
    }

    if (m_useQmlDebugger || m_useQmlProfiler) {
        // currently forward to same port on device and host
        const QString port = QString::fromLatin1("tcp:%1").arg(m_qmlPort);
        QProcess adb;
        adb.start(m_adb, selector() << _("forward") << port << port);
        if (!adb.waitForStarted()) {
            emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.").arg(adb.errorString()));
            return;
        }
        if (!adb.waitForFinished()) {
            emit remoteProcessFinished(tr("Failed to forward QML debugging ports."));
            return;
        }
        args << _("-e") << _("qml_debug") << _("true");
        args << _("-e") << _("qmljsdebugger") << QString::fromLatin1("port:%1,block").arg(m_qmlPort);
    }
    if (m_useLocalQtLibs) {
        args << _("-e") << _("use_local_qt_libs") << _("true");
        args << _("-e") << _("libs_prefix") << _("/data/local/tmp/qt/");
        args << _("-e") << _("load_local_libs") << m_localLibs;
        args << _("-e") << _("load_local_jars") << m_localJars;
        if (!m_localJarsInitClasses.isEmpty())
            args << _("-e") << _("static_init_classes") << m_localJarsInitClasses;
    }

    QProcess adb;
    adb.start(m_adb, args);
    if (!adb.waitForStarted()) {
        emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.").arg(adb.errorString()));
        return;
    }
    if (!adb.waitForFinished(-1)) {
        adb.terminate();
        emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
        return;
    }

    if (m_useCppDebugger) {

        // Handling ping.
        for (int i = 0; ; ++i) {
            QTemporaryFile tmp(_("pingpong"));
            tmp.open();
            tmp.close();

            QProcess process;
            process.start(m_adb, selector() << _("pull") << m_pingFile << tmp.fileName());
            process.waitForFinished();

            QFile res(tmp.fileName());
            const bool doBreak = res.size();
            res.remove();
            if (doBreak)
                break;

            if (i == 20) {
                emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
                return;
            }
            qDebug() << "WAITING FOR " << tmp.fileName();
            QThread::msleep(500);
        }

    }

    QByteArray psOut = runPs();
    m_processPID = extractPid(m_packageName, psOut);

    if (m_processPID == -1) {
        emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
        return;
    }

    QMetaObject::invokeMethod(&m_checkPIDTimer, "start");

    m_wasStarted = true;
    if (m_useCppDebugger) {
        // This will be funneled to the engine to actually start and attach
        // gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
        QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort);
        emit remoteServerRunning(serverChannel, m_processPID);
    } else if (m_useQmlDebugger) {
        // This will be funneled to the engine to actually start and attach
        // gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
        QByteArray serverChannel = QByteArray::number(m_qmlPort);
        emit remoteServerRunning(serverChannel, m_processPID);
    } else if (m_useQmlProfiler) {
        emit remoteProcessStarted(m_qmlPort);
    } else {
        // Start without debugging.
        emit remoteProcessStarted(-1, -1);
    }
}

void AndroidRunner::handleRemoteDebuggerRunning()
{
    if (m_useCppDebugger) {
        QTemporaryFile tmp(_("pingpong"));
        tmp.open();

        QProcess process;
        process.start(m_adb, selector() << _("push") << tmp.fileName() << m_pongFile);
        process.waitForFinished();

        QTC_CHECK(m_processPID != -1);
    }
    emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort);
}

void AndroidRunner::stop()
{
    QMutexLocker locker(&m_mutex);
    m_checkPIDTimer.stop();
    if (m_processPID != -1) {
        killPID();
        emit remoteProcessFinished(tr("\n\n'%1' terminated.").arg(m_packageName));
    }
    //QObject::disconnect(&m_adbLogcatProcess, 0, this, 0);
    m_adbLogcatProcess.kill();
    m_adbLogcatProcess.waitForFinished();
}

void AndroidRunner::logcatReadStandardError()
{
    emit remoteErrorOutput(m_adbLogcatProcess.readAllStandardError());
}

void AndroidRunner::logcatReadStandardOutput()
{
    m_logcat += m_adbLogcatProcess.readAllStandardOutput();
    bool keepLastLine = m_logcat.endsWith('\n');
    QByteArray line;
    QByteArray pid(QString::fromLatin1("%1):").arg(m_processPID).toLatin1());
    foreach (line, m_logcat.split('\n')) {
        if (!line.contains(pid))
            continue;
        if (line.endsWith('\r'))
            line.chop(1);
        line.append('\n');
        if (line.startsWith("E/"))
            emit remoteErrorOutput(line);
        else
            emit remoteOutput(line);

    }
    if (keepLastLine)
        m_logcat = line;
}

void AndroidRunner::adbKill(qint64 pid)
{
    {
        QProcess process;
        process.start(m_adb, selector() << _("shell")
            << _("kill") << QLatin1String("-9") << QString::number(pid));
        process.waitForFinished();
    }
    {
        QProcess process;
        process.start(m_adb, selector() << _("shell")
            << _("run-as") << m_packageName
            << _("kill") << QLatin1String("-9") << QString::number(pid));
        process.waitForFinished();
    }
}

QString AndroidRunner::displayName() const
{
    return m_packageName;
}

} // namespace Internal
} // namespace Android
<commit_msg>Android: Make warnings in application output red too<commit_after>/**************************************************************************
**
** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "androidrunner.h"

#include "androiddeploystep.h"
#include "androiddeployqtstep.h"
#include "androidconfigurations.h"
#include "androidglobal.h"
#include "androidrunconfiguration.h"
#include "androidmanager.h"

#include <debugger/debuggerrunconfigurationaspect.h>
#include <projectexplorer/target.h>
#include <utils/qtcassert.h>

#include <QTime>
#include <QtConcurrentRun>
#include <QTemporaryFile>
#include <QTcpServer>

namespace Android {
namespace Internal {

typedef QLatin1String _;

AndroidRunner::AndroidRunner(QObject *parent,
                             AndroidRunConfiguration *runConfig,
                             ProjectExplorer::RunMode runMode)
    : QThread(parent)
{
    m_wasStarted = false;
    Debugger::DebuggerRunConfigurationAspect *aspect
            = runConfig->extraAspect<Debugger::DebuggerRunConfigurationAspect>();
    const bool debuggingMode = runMode == ProjectExplorer::DebugRunMode;
    m_useCppDebugger = debuggingMode && aspect->useCppDebugger();
    m_useQmlDebugger = debuggingMode && aspect->useQmlDebugger();
    QString channel = runConfig->remoteChannel();
    QTC_CHECK(channel.startsWith(QLatin1Char(':')));
    m_localGdbServerPort = channel.mid(1).toUShort();
    QTC_CHECK(m_localGdbServerPort);
    m_useQmlProfiler = runMode == ProjectExplorer::QmlProfilerRunMode;
    if (m_useQmlDebugger || m_useQmlProfiler) {
        QTcpServer server;
        QTC_ASSERT(server.listen(QHostAddress::LocalHost)
                   || server.listen(QHostAddress::LocalHostIPv6),
                   qDebug() << tr("No free ports available on host for QML debugging."));
        m_qmlPort = server.serverPort();
    }
    ProjectExplorer::Target *target = runConfig->target();
    m_useLocalQtLibs = AndroidManager::useLocalLibs(target);
    if (m_useLocalQtLibs) {
        int deviceApiLevel = AndroidManager::minimumSDK(target);
        m_localLibs = AndroidManager::loadLocalLibs(target, deviceApiLevel);
        m_localJars = AndroidManager::loadLocalJars(target, deviceApiLevel);
        m_localJarsInitClasses = AndroidManager::loadLocalJarsInitClasses(target, deviceApiLevel);
    }
    m_intentName = AndroidManager::intentName(target);
    m_packageName = m_intentName.left(m_intentName.indexOf(QLatin1Char('/')));

    m_deviceSerialNumber = AndroidManager::deviceSerialNumber(target);
    m_processPID = -1;
    m_adb = AndroidConfigurations::instance().adbToolPath().toString();
    m_selector = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber);

    QString packageDir = _("/data/data/") + m_packageName;
    m_pingFile = packageDir + _("/debug-ping");
    m_pongFile = _("/data/local/tmp/qt/debug-pong-") + m_packageName;
    m_gdbserverSocket = packageDir + _("/debug-socket");
    m_gdbserverPath = packageDir + _("/lib/gdbserver");
    m_gdbserverCommand = m_gdbserverPath + _(" --multi +") + m_gdbserverSocket;
    // Detect busybox, as we need to pass -w to ps to get wide output.
    QProcess psProc;
    psProc.start(m_adb, selector() << _("shell") << _("readlink") << _("$(which ps)"));
    psProc.waitForFinished();
    QByteArray which = psProc.readAll();
    m_isBusyBox = which.startsWith("busybox");

    m_checkPIDTimer.setInterval(1000);

    connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardOutput()), SLOT(logcatReadStandardOutput()));
    connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardError()), SLOT(logcatReadStandardError()));
    connect(&m_checkPIDTimer, SIGNAL(timeout()), SLOT(checkPID()));
}

AndroidRunner::~AndroidRunner()
{
    //stop();
}

static int extractPidFromChunk(const QByteArray &chunk, int from)
{
    int pos1 = chunk.indexOf(' ', from);
    if (pos1 == -1)
        return -1;
    while (chunk[pos1] == ' ')
        ++pos1;
    int pos3 = chunk.indexOf(' ', pos1);
    int pid = chunk.mid(pos1, pos3 - pos1).toInt();
    return pid;
}

static int extractPid(const QString &exeName, const QByteArray &psOutput)
{
    const QByteArray needle = exeName.toUtf8() + '\r';
    const int to = psOutput.indexOf(needle);
    if (to == -1)
        return -1;
    const int from = psOutput.lastIndexOf('\n', to);
    if (from == -1)
        return -1;
    return extractPidFromChunk(psOutput, from);
}

QByteArray AndroidRunner::runPs()
{
    QProcess psProc;
    QStringList args = m_selector;
    args << _("shell") << _("ps");
    if (m_isBusyBox)
        args << _("-w");

    psProc.start(m_adb, args);
    psProc.waitForFinished();
    return psProc.readAll();
}

void AndroidRunner::checkPID()
{
    if (!m_wasStarted)
        return;
    QByteArray psOut = runPs();
    m_processPID = extractPid(m_packageName, psOut);
    if (m_processPID == -1) {
        m_checkPIDTimer.stop();
        emit remoteProcessFinished(tr("\n\n'%1' died.").arg(m_packageName));
    }
}

void AndroidRunner::forceStop()
{
    QProcess proc;
    proc.start(m_adb, selector() << _("shell") << _("am") << _("force-stop"));
    proc.waitForFinished();
}

void AndroidRunner::killPID()
{
    const QByteArray out = runPs();
    int from = 0;
    while (1) {
        const int to = out.indexOf('\n', from);
        if (to == -1)
            break;
        QString line = QString::fromUtf8(out.data() + from, to - from - 1);
        if (line.endsWith(m_packageName) || line.endsWith(m_gdbserverPath)) {
            int pid = extractPidFromChunk(out, from);
            adbKill(pid);
        }
        from = to + 1;
    }
}

void AndroidRunner::start()
{
    m_adbLogcatProcess.start(m_adb, selector() << _("logcat"));
    m_wasStarted = false;
    QtConcurrent::run(this, &AndroidRunner::asyncStart);
}

void AndroidRunner::asyncStart()
{
    QMutexLocker locker(&m_mutex);
    forceStop();
    killPID();

    if (m_useCppDebugger) {
        // Remove pong file.
        QProcess adb;
        adb.start(m_adb, selector() << _("shell") << _("rm") << m_pongFile);
        adb.waitForFinished();
    }

    QStringList args = selector();
    args << _("shell") << _("am") << _("start") << _("-n") << m_intentName;

    if (m_useCppDebugger) {
        QProcess adb;
        adb.start(m_adb, selector() << _("forward")
                  << QString::fromLatin1("tcp:%1").arg(m_localGdbServerPort)
                  << _("localfilesystem:") + m_gdbserverSocket);
        if (!adb.waitForStarted()) {
            emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(adb.errorString()));
            return;
        }
        if (!adb.waitForFinished(-1)) {
            emit remoteProcessFinished(tr("Failed to forward C++ debugging ports."));
            return;
        }

        args << _("-e") << _("debug_ping") << _("true");
        args << _("-e") << _("ping_file") << m_pingFile;
        args << _("-e") << _("pong_file") << m_pongFile;
        args << _("-e") << _("gdbserver_command") << m_gdbserverCommand;
        args << _("-e") << _("gdbserver_socket") << m_gdbserverSocket;
    }

    if (m_useQmlDebugger || m_useQmlProfiler) {
        // currently forward to same port on device and host
        const QString port = QString::fromLatin1("tcp:%1").arg(m_qmlPort);
        QProcess adb;
        adb.start(m_adb, selector() << _("forward") << port << port);
        if (!adb.waitForStarted()) {
            emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.").arg(adb.errorString()));
            return;
        }
        if (!adb.waitForFinished()) {
            emit remoteProcessFinished(tr("Failed to forward QML debugging ports."));
            return;
        }
        args << _("-e") << _("qml_debug") << _("true");
        args << _("-e") << _("qmljsdebugger") << QString::fromLatin1("port:%1,block").arg(m_qmlPort);
    }
    if (m_useLocalQtLibs) {
        args << _("-e") << _("use_local_qt_libs") << _("true");
        args << _("-e") << _("libs_prefix") << _("/data/local/tmp/qt/");
        args << _("-e") << _("load_local_libs") << m_localLibs;
        args << _("-e") << _("load_local_jars") << m_localJars;
        if (!m_localJarsInitClasses.isEmpty())
            args << _("-e") << _("static_init_classes") << m_localJarsInitClasses;
    }

    QProcess adb;
    adb.start(m_adb, args);
    if (!adb.waitForStarted()) {
        emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.").arg(adb.errorString()));
        return;
    }
    if (!adb.waitForFinished(-1)) {
        adb.terminate();
        emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
        return;
    }

    if (m_useCppDebugger) {

        // Handling ping.
        for (int i = 0; ; ++i) {
            QTemporaryFile tmp(_("pingpong"));
            tmp.open();
            tmp.close();

            QProcess process;
            process.start(m_adb, selector() << _("pull") << m_pingFile << tmp.fileName());
            process.waitForFinished();

            QFile res(tmp.fileName());
            const bool doBreak = res.size();
            res.remove();
            if (doBreak)
                break;

            if (i == 20) {
                emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
                return;
            }
            qDebug() << "WAITING FOR " << tmp.fileName();
            QThread::msleep(500);
        }

    }

    QByteArray psOut = runPs();
    m_processPID = extractPid(m_packageName, psOut);

    if (m_processPID == -1) {
        emit remoteProcessFinished(tr("Unable to start '%1'.").arg(m_packageName));
        return;
    }

    QMetaObject::invokeMethod(&m_checkPIDTimer, "start");

    m_wasStarted = true;
    if (m_useCppDebugger) {
        // This will be funneled to the engine to actually start and attach
        // gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
        QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort);
        emit remoteServerRunning(serverChannel, m_processPID);
    } else if (m_useQmlDebugger) {
        // This will be funneled to the engine to actually start and attach
        // gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
        QByteArray serverChannel = QByteArray::number(m_qmlPort);
        emit remoteServerRunning(serverChannel, m_processPID);
    } else if (m_useQmlProfiler) {
        emit remoteProcessStarted(m_qmlPort);
    } else {
        // Start without debugging.
        emit remoteProcessStarted(-1, -1);
    }
}

void AndroidRunner::handleRemoteDebuggerRunning()
{
    if (m_useCppDebugger) {
        QTemporaryFile tmp(_("pingpong"));
        tmp.open();

        QProcess process;
        process.start(m_adb, selector() << _("push") << tmp.fileName() << m_pongFile);
        process.waitForFinished();

        QTC_CHECK(m_processPID != -1);
    }
    emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort);
}

void AndroidRunner::stop()
{
    QMutexLocker locker(&m_mutex);
    m_checkPIDTimer.stop();
    if (m_processPID != -1) {
        killPID();
        emit remoteProcessFinished(tr("\n\n'%1' terminated.").arg(m_packageName));
    }
    //QObject::disconnect(&m_adbLogcatProcess, 0, this, 0);
    m_adbLogcatProcess.kill();
    m_adbLogcatProcess.waitForFinished();
}

void AndroidRunner::logcatReadStandardError()
{
    emit remoteErrorOutput(m_adbLogcatProcess.readAllStandardError());
}

void AndroidRunner::logcatReadStandardOutput()
{
    m_logcat += m_adbLogcatProcess.readAllStandardOutput();
    bool keepLastLine = m_logcat.endsWith('\n');
    QByteArray line;
    QByteArray pid(QString::fromLatin1("%1):").arg(m_processPID).toLatin1());
    foreach (line, m_logcat.split('\n')) {
        if (!line.contains(pid))
            continue;
        if (line.endsWith('\r'))
            line.chop(1);
        line.append('\n');
        if (line.startsWith("E/")
                || line.startsWith("D/Qt")
                || line.startsWith("W/"))
            emit remoteErrorOutput(line);
        else
            emit remoteOutput(line);

    }
    if (keepLastLine)
        m_logcat = line;
}

void AndroidRunner::adbKill(qint64 pid)
{
    {
        QProcess process;
        process.start(m_adb, selector() << _("shell")
            << _("kill") << QLatin1String("-9") << QString::number(pid));
        process.waitForFinished();
    }
    {
        QProcess process;
        process.start(m_adb, selector() << _("shell")
            << _("run-as") << m_packageName
            << _("kill") << QLatin1String("-9") << QString::number(pid));
        process.waitForFinished();
    }
}

QString AndroidRunner::displayName() const
{
    return m_packageName;
}

} // namespace Internal
} // namespace Android
<|endoftext|>


<commit_before>/** @file
    @brief OSVR plugin for LYRobotix Nolo trackers

    @date 2017

    @author
    Yann Vernier
    <http://yann.vernier.se>
*/

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

// Internal Includes
#include <osvr/PluginKit/PluginKit.h>
#include <osvr/PluginKit/AnalogInterfaceC.h>
#include <osvr/PluginKit/ButtonInterfaceC.h>
#include <osvr/PluginKit/TrackerInterfaceC.h>

// Generated JSON header file
#include "com_osvr_Nolo_json.h"

// Library/third-party includes
#include <hidapi.h>

// Standard includes
#include <iostream>
#include <wchar.h>

// Anonymous namespace to avoid symbol collision
namespace {

  // btea_decrypt function
  #include "btea.c"

  #if 0
  void hexdump(unsigned char *data, int len) {
    char fill = std::cout.fill();
    std::streamsize w = std::cout.width();
    std::ios_base::fmtflags f = std::cout.flags();
    for (int i=0; i<len; i++) {
      std::cout << std::setw(2) << std::setfill('0')
		<< std::hex << (int)data[i];
      if (i%8==7)
	std::cout << " ";
    }
    std::cout << std::endl;
    std::cout.fill(fill);
    std::cout.width(w);
    std::cout.flags(f);
  };
  #endif

static int NUM_AXIS    = 4;
static int NUM_BUTTONS = 6;
class NoloDevice {
  public:
    NoloDevice(OSVR_PluginRegContext ctx,
	       char *path) {
        /// Create the initialization options
        OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

	std::cout << "Nolo: opening " << path <<
	  " for " << this << std::endl;
	m_hid = hid_open_path(path);

        /// Indicate that we'll want 7 analog channels.
        //osvrDeviceAnalogConfigure(opts, &m_analog, 2*3+1);
	// update this to 9 analog channels to include the trigger
        osvrDeviceAnalogConfigure(opts, &m_analog, NUM_AXIS*2+1);
	/// And 6 buttons per controller
        osvrDeviceButtonConfigure(opts, &m_button, 2*NUM_BUTTONS);
	/// And tracker capability
        osvrDeviceTrackerConfigure(opts, &m_tracker);

        /// Create the device token with the options
        m_dev.initAsync(ctx, "LYRobotix Nolo", opts);

        /// Send JSON descriptor
        m_dev.sendJsonDescriptor(com_osvr_Nolo_json);

        /// Register update callback
        m_dev.registerUpdateCallback(this);
    }
    ~NoloDevice() {
      std::cout << "Nolo: deleting " << this << std::endl;
      hid_close(m_hid);
    }

    OSVR_ReturnCode update() {
      unsigned char buf[64];
      const int cryptwords = (64-4)/4, cryptoffset=1;
      static const uint32_t key[4] =
	{0x875bcc51, 0xa7637a66, 0x50960967, 0xf8536c51};
      uint32_t cryptpart[cryptwords];
      int i;

      // TODO: timestamp frame received?
      if (!m_hid)
	 OSVR_RETURN_FAILURE;
      //std::cout << "Reading HID data from " << m_hid << std::endl;
      if(hid_read(m_hid, buf, sizeof buf) != sizeof buf)
	return OSVR_RETURN_FAILURE;
      osvrTimeValueGetNow(&m_lastreport_time);

      //std::cout << "R ";
      //hexdump(buf, sizeof buf);
      // Decrypt encrypted portion
      for (i=0; i<cryptwords; i++) {
	cryptpart[i] =
	  ((uint32_t)buf[cryptoffset+4*i  ])<<0  |
	  ((uint32_t)buf[cryptoffset+4*i+1])<<8  |
	  ((uint32_t)buf[cryptoffset+4*i+2])<<16 |
	  ((uint32_t)buf[cryptoffset+4*i+3])<<24;
      }
      btea_decrypt(cryptpart, cryptwords, 1, key);
      for (i=0; i<cryptwords; i++) {
	buf[cryptoffset+4*i  ] = cryptpart[i]>>0;
	buf[cryptoffset+4*i+1] = cryptpart[i]>>8;
	buf[cryptoffset+4*i+2] = cryptpart[i]>>16;
	buf[cryptoffset+4*i+3] = cryptpart[i]>>24;
      }
      //std::cout << "D ";
      //hexdump(buf, sizeof buf);

      switch (buf[0]) {
      case 0xa5:  // controllers frame
	decodeControllerCV1(0, buf+1);
	decodeControllerCV1(1, buf+64-controllerLength);
	break;
      case 0xa6:
	decodeHeadsetMarkerCV1(buf+0x15);
	decodeBaseStationCV1(buf+0x36);
	break;
      }
      return OSVR_RETURN_SUCCESS;
    }

    // Sets vibration strength in percent
    int setVibration(unsigned char left,
		     unsigned char right) {
      unsigned char data[4] = {0xaa, 0x66, left, right};
      return hid_write(m_hid, data, sizeof data);
    }

  private:
    void decodePosition(const unsigned char *data,
			OSVR_PositionState *pos) {
      const double scale = 0.0001;
      osvrVec3SetX(pos, scale * (int16_t)(data[0]<<8 | data[1]));
      osvrVec3SetY(pos, scale * (int16_t)(data[2]<<8 | data[3]));
      osvrVec3SetZ(pos, scale *          (data[4]<<8 | data[5]));
    }
    void decodeOrientation(const unsigned char *data,
			   OSVR_OrientationState *quat) {
      double w,i,j,k, scale;
      // CV1 order
      w = (int16_t)(data[0]<<8 | data[1]);
      i = (int16_t)(data[2]<<8 | data[3]);
      j = (int16_t)(data[4]<<8 | data[5]);
      k = (int16_t)(data[6]<<8 | data[7]);
      // Normalize (unknown if scale is constant)
      //scale = 1.0/sqrt(i*i+j*j+k*k+w*w);
      // Turns out it is fixed point. But the android driver author
      // either didn't know, or didn't trust it.
      // Unknown if normalizing it helps OSVR!
      scale = 1.0 / 16384;
      //std::cout << "Scale: " << scale << std::endl;
      w *= scale;
      i *= scale;
      j *= scale;
      k *= scale;
      // Reorder
      osvrQuatSetW(quat, w);
      osvrQuatSetX(quat, i);
      osvrQuatSetY(quat, k);
      osvrQuatSetZ(quat, -j);
    }
    void decodeControllerCV1(int idx, unsigned char *data) {
      OSVR_PoseState pose;
      uint8_t buttons, bit;
      int trigger_pressed = 0;

      if (data[0] != 2 || data[1] != 1) {
	// Unknown version
	/* Happens when controllers aren't on.
	std::cout << "Nolo: Unknown controller " << idx
		  << " version " << (int)data[0] << " " << (int)data[1]
		  << std::endl;
	*/
	return;
      }

      decodePosition(data+3, &pose.translation);
      decodeOrientation(data+3+3*2, &pose.rotation);

      osvrDeviceTrackerSendPoseTimestamped(m_dev, m_tracker, &pose, 2+idx, &m_lastreport_time);

      buttons = data[3+3*2+4*2];
      // TODO: report buttons for both controllers in one call?
      for (bit=0; bit<NUM_BUTTONS; bit++){
	osvrDeviceButtonSetValueTimestamped(m_dev, m_button,
				 (buttons & 1<<bit ? OSVR_BUTTON_PRESSED
				  : OSVR_BUTTON_NOT_PRESSED), idx*6+bit,
				 &m_lastreport_time);
	if(bit == 1){
	    if(buttons & 1<<bit){
		trigger_pressed = 1;
	    }else{
		trigger_pressed = 0;
	    }
	}
      }
      // next byte is touch ID bitmask (identical to buttons bit 5)

      // Touch X and Y coordinates
      // //assumes 0 to 255
      // normalize from -1 to 1
      // z = 2*[x - min / (max - min) - 1]
      // z = 2*(x - 0 / (255 - 0) - 1]
      // z = 2*(x/255) -1
      // normalize 0 to 1
      // x/255
      double axis_value = 2*data[3+3*2+4*2+2]/255.0 - 1;
      // invert axis
      axis_value *= -1;
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value,   idx*NUM_AXIS+0, &m_lastreport_time);
      axis_value = 2*((int)data[3+3*2+4*2+2+1])/255.0 -1;
      axis_value *= -1;
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value, idx*NUM_AXIS+1, &m_lastreport_time);
      // trigger
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, trigger_pressed, idx*NUM_AXIS+2, &m_lastreport_time);
      // battery level
      axis_value = data[3+3*2+4*2+2+2]/255.0;
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value, idx*NUM_AXIS+3, &m_lastreport_time);
      /*
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, data[3+3*2+4*2+2],   idx*3+0, &m_lastreport_time);
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, data[3+3*2+4*2+2+1], idx*3+1, &m_lastreport_time);
      // battery level
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, data[3+3*2+4*2+2+2], idx*3+2, &m_lastreport_time);
      */
    }
    void decodeHeadsetMarkerCV1(unsigned char *data) {
      if (data[0] != 2 || data[1] != 1) {
	/* Happens with corrupt packages (mixed with controller data)
	std::cout << "Nolo: Unknown headset marker"
		  << " version " << (int)data[0] << " " << (int)data[1]
		  << std::endl;
	*/
	// Unknown version
	return;
      }

      OSVR_PositionState home;
      OSVR_PoseState hmd;

      decodePosition(data+3, &hmd.translation);
      decodePosition(data+3+3*2, &home);
      decodeOrientation(data+3+2*3*2+1, &hmd.rotation);

      // Tracker viewer kept using the home for head.
      // Something wrong with how they handle the descriptors.
      osvrDeviceTrackerSendPositionTimestamped(m_dev, m_tracker, &home, 0,
		      &m_lastreport_time);

      osvrDeviceTrackerSendPoseTimestamped(m_dev, m_tracker, &hmd, 1,
		      &m_lastreport_time);
    }
    void decodeBaseStationCV1(unsigned char *data) {
      if (data[0] != 2 || data[1] != 1)
	// Unknown version
	return;

      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, data[2], 2*NUM_AXIS,
		      &m_lastreport_time);
    }

    static const int controllerLength = 3 + (3+4)*2 + 2 + 2 + 1;
    osvr::pluginkit::DeviceToken m_dev;
    hid_device* m_hid;
    OSVR_AnalogDeviceInterface m_analog;
    OSVR_ButtonDeviceInterface m_button;
    OSVR_TrackerDeviceInterface m_tracker;
    OSVR_TimeValue m_lastreport_time;
};

class HardwareDetection {
  public:
    HardwareDetection() : m_found(false) {}
    OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx) {

      // TODO: probe for new devices only
      if (m_found)
	return OSVR_RETURN_SUCCESS;

      struct hid_device_info *hid_devices, *cur_dev;
      hid_devices = hid_enumerate(0x0483, 0x5750);
      if (!hid_devices)
	return OSVR_RETURN_FAILURE;

      for (cur_dev = hid_devices; cur_dev; cur_dev = cur_dev->next) {
	if (wcscmp(cur_dev->manufacturer_string, L"LYRobotix")==0 &&
	    wcscmp(cur_dev->product_string, L"NOLO")==0) {
	  /// TODO: Distinguish Headset Marker from other parts?
	  /// Create our device object
	  osvr::pluginkit::registerObjectForDeletion
	    (ctx, new NoloDevice(ctx, cur_dev->path));
	  m_found = true;
	}
      }

      hid_free_enumeration(hid_devices);
      return OSVR_RETURN_SUCCESS;
    }

  private:
    /// @brief Have we found our device yet? (this limits the plugin to one
    /// instance)
    bool m_found;
};
} // namespace

OSVR_PLUGIN(com_osvr_Nolo) {
    hid_init();
    osvr::pluginkit::PluginContext context(ctx);

    /// Register a detection callback function object.
    context.registerHardwareDetectCallback(new HardwareDetection());

    return OSVR_RETURN_SUCCESS;
}
<commit_msg>Removed comments.<commit_after>/** @file
    @brief OSVR plugin for LYRobotix Nolo trackers

    @date 2017

    @author
    Yann Vernier
    <http://yann.vernier.se>
*/

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

// Internal Includes
#include <osvr/PluginKit/PluginKit.h>
#include <osvr/PluginKit/AnalogInterfaceC.h>
#include <osvr/PluginKit/ButtonInterfaceC.h>
#include <osvr/PluginKit/TrackerInterfaceC.h>

// Generated JSON header file
#include "com_osvr_Nolo_json.h"

// Library/third-party includes
#include <hidapi.h>

// Standard includes
#include <iostream>
#include <wchar.h>

// Anonymous namespace to avoid symbol collision
namespace {

  // btea_decrypt function
  #include "btea.c"

  #if 0
  void hexdump(unsigned char *data, int len) {
    char fill = std::cout.fill();
    std::streamsize w = std::cout.width();
    std::ios_base::fmtflags f = std::cout.flags();
    for (int i=0; i<len; i++) {
      std::cout << std::setw(2) << std::setfill('0')
		<< std::hex << (int)data[i];
      if (i%8==7)
	std::cout << " ";
    }
    std::cout << std::endl;
    std::cout.fill(fill);
    std::cout.width(w);
    std::cout.flags(f);
  };
  #endif

static int NUM_AXIS    = 4;
static int NUM_BUTTONS = 6;
class NoloDevice {
  public:
    NoloDevice(OSVR_PluginRegContext ctx,
	       char *path) {
        /// Create the initialization options
        OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

	std::cout << "Nolo: opening " << path <<
	  " for " << this << std::endl;
	m_hid = hid_open_path(path);

        /// Indicate that we'll want 7 analog channels.
        //osvrDeviceAnalogConfigure(opts, &m_analog, 2*3+1);
	// update this to 9 analog channels to include the trigger
        osvrDeviceAnalogConfigure(opts, &m_analog, NUM_AXIS*2+1);
	/// And 6 buttons per controller
        osvrDeviceButtonConfigure(opts, &m_button, 2*NUM_BUTTONS);
	/// And tracker capability
        osvrDeviceTrackerConfigure(opts, &m_tracker);

        /// Create the device token with the options
        m_dev.initAsync(ctx, "LYRobotix Nolo", opts);

        /// Send JSON descriptor
        m_dev.sendJsonDescriptor(com_osvr_Nolo_json);

        /// Register update callback
        m_dev.registerUpdateCallback(this);
    }
    ~NoloDevice() {
      std::cout << "Nolo: deleting " << this << std::endl;
      hid_close(m_hid);
    }

    OSVR_ReturnCode update() {
      unsigned char buf[64];
      const int cryptwords = (64-4)/4, cryptoffset=1;
      static const uint32_t key[4] =
	{0x875bcc51, 0xa7637a66, 0x50960967, 0xf8536c51};
      uint32_t cryptpart[cryptwords];
      int i;

      // TODO: timestamp frame received?
      if (!m_hid)
	 OSVR_RETURN_FAILURE;
      //std::cout << "Reading HID data from " << m_hid << std::endl;
      if(hid_read(m_hid, buf, sizeof buf) != sizeof buf)
	return OSVR_RETURN_FAILURE;
      osvrTimeValueGetNow(&m_lastreport_time);

      //std::cout << "R ";
      //hexdump(buf, sizeof buf);
      // Decrypt encrypted portion
      for (i=0; i<cryptwords; i++) {
	cryptpart[i] =
	  ((uint32_t)buf[cryptoffset+4*i  ])<<0  |
	  ((uint32_t)buf[cryptoffset+4*i+1])<<8  |
	  ((uint32_t)buf[cryptoffset+4*i+2])<<16 |
	  ((uint32_t)buf[cryptoffset+4*i+3])<<24;
      }
      btea_decrypt(cryptpart, cryptwords, 1, key);
      for (i=0; i<cryptwords; i++) {
	buf[cryptoffset+4*i  ] = cryptpart[i]>>0;
	buf[cryptoffset+4*i+1] = cryptpart[i]>>8;
	buf[cryptoffset+4*i+2] = cryptpart[i]>>16;
	buf[cryptoffset+4*i+3] = cryptpart[i]>>24;
      }
      //std::cout << "D ";
      //hexdump(buf, sizeof buf);

      switch (buf[0]) {
      case 0xa5:  // controllers frame
	decodeControllerCV1(0, buf+1);
	decodeControllerCV1(1, buf+64-controllerLength);
	break;
      case 0xa6:
	decodeHeadsetMarkerCV1(buf+0x15);
	decodeBaseStationCV1(buf+0x36);
	break;
      }
      return OSVR_RETURN_SUCCESS;
    }

    // Sets vibration strength in percent
    int setVibration(unsigned char left,
		     unsigned char right) {
      unsigned char data[4] = {0xaa, 0x66, left, right};
      return hid_write(m_hid, data, sizeof data);
    }

  private:
    void decodePosition(const unsigned char *data,
			OSVR_PositionState *pos) {
      const double scale = 0.0001;
      osvrVec3SetX(pos, scale * (int16_t)(data[0]<<8 | data[1]));
      osvrVec3SetY(pos, scale * (int16_t)(data[2]<<8 | data[3]));
      osvrVec3SetZ(pos, scale *          (data[4]<<8 | data[5]));
    }
    void decodeOrientation(const unsigned char *data,
			   OSVR_OrientationState *quat) {
      double w,i,j,k, scale;
      // CV1 order
      w = (int16_t)(data[0]<<8 | data[1]);
      i = (int16_t)(data[2]<<8 | data[3]);
      j = (int16_t)(data[4]<<8 | data[5]);
      k = (int16_t)(data[6]<<8 | data[7]);
      // Normalize (unknown if scale is constant)
      //scale = 1.0/sqrt(i*i+j*j+k*k+w*w);
      // Turns out it is fixed point. But the android driver author
      // either didn't know, or didn't trust it.
      // Unknown if normalizing it helps OSVR!
      scale = 1.0 / 16384;
      //std::cout << "Scale: " << scale << std::endl;
      w *= scale;
      i *= scale;
      j *= scale;
      k *= scale;
      // Reorder
      osvrQuatSetW(quat, w);
      osvrQuatSetX(quat, i);
      osvrQuatSetY(quat, k);
      osvrQuatSetZ(quat, -j);
    }
    void decodeControllerCV1(int idx, unsigned char *data) {
      OSVR_PoseState pose;
      uint8_t buttons, bit;
      int trigger_pressed = 0;

      if (data[0] != 2 || data[1] != 1) {
	// Unknown version
	/* Happens when controllers aren't on.
	std::cout << "Nolo: Unknown controller " << idx
		  << " version " << (int)data[0] << " " << (int)data[1]
		  << std::endl;
	*/
	return;
      }

      decodePosition(data+3, &pose.translation);
      decodeOrientation(data+3+3*2, &pose.rotation);

      osvrDeviceTrackerSendPoseTimestamped(m_dev, m_tracker, &pose, 2+idx, &m_lastreport_time);

      buttons = data[3+3*2+4*2];
      // TODO: report buttons for both controllers in one call?
      for (bit=0; bit<NUM_BUTTONS; bit++){
	osvrDeviceButtonSetValueTimestamped(m_dev, m_button,
				 (buttons & 1<<bit ? OSVR_BUTTON_PRESSED
				  : OSVR_BUTTON_NOT_PRESSED), idx*6+bit,
				 &m_lastreport_time);
	if(bit == 1){
	    if(buttons & 1<<bit){
		trigger_pressed = 1;
	    }else{
		trigger_pressed = 0;
	    }
	}
      }
      // next byte is touch ID bitmask (identical to buttons bit 5)

      // Touch X and Y coordinates
      // //assumes 0 to 255
      // normalize from -1 to 1
      // z = 2*[x - min / (max - min) - 1]
      // z = 2*(x - 0 / (255 - 0) - 1]
      // z = 2*(x/255) -1
      // normalize 0 to 1
      // x/255
      double axis_value = 2*data[3+3*2+4*2+2]/255.0 - 1;
      // invert axis
      axis_value *= -1;
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value,   idx*NUM_AXIS+0, &m_lastreport_time);
      axis_value = 2*((int)data[3+3*2+4*2+2+1])/255.0 -1;
      axis_value *= -1;
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value, idx*NUM_AXIS+1, &m_lastreport_time);
      // trigger
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, trigger_pressed, idx*NUM_AXIS+2, &m_lastreport_time);
      // battery level
      axis_value = data[3+3*2+4*2+2+2]/255.0;
      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, axis_value, idx*NUM_AXIS+3, &m_lastreport_time);
    }
    void decodeHeadsetMarkerCV1(unsigned char *data) {
      if (data[0] != 2 || data[1] != 1) {
	/* Happens with corrupt packages (mixed with controller data)
	std::cout << "Nolo: Unknown headset marker"
		  << " version " << (int)data[0] << " " << (int)data[1]
		  << std::endl;
	*/
	// Unknown version
	return;
      }

      OSVR_PositionState home;
      OSVR_PoseState hmd;

      decodePosition(data+3, &hmd.translation);
      decodePosition(data+3+3*2, &home);
      decodeOrientation(data+3+2*3*2+1, &hmd.rotation);

      // Tracker viewer kept using the home for head.
      // Something wrong with how they handle the descriptors.
      osvrDeviceTrackerSendPositionTimestamped(m_dev, m_tracker, &home, 0,
		      &m_lastreport_time);

      osvrDeviceTrackerSendPoseTimestamped(m_dev, m_tracker, &hmd, 1,
		      &m_lastreport_time);
    }
    void decodeBaseStationCV1(unsigned char *data) {
      if (data[0] != 2 || data[1] != 1)
	// Unknown version
	return;

      osvrDeviceAnalogSetValueTimestamped(m_dev, m_analog, data[2], 2*NUM_AXIS,
		      &m_lastreport_time);
    }

    static const int controllerLength = 3 + (3+4)*2 + 2 + 2 + 1;
    osvr::pluginkit::DeviceToken m_dev;
    hid_device* m_hid;
    OSVR_AnalogDeviceInterface m_analog;
    OSVR_ButtonDeviceInterface m_button;
    OSVR_TrackerDeviceInterface m_tracker;
    OSVR_TimeValue m_lastreport_time;
};

class HardwareDetection {
  public:
    HardwareDetection() : m_found(false) {}
    OSVR_ReturnCode operator()(OSVR_PluginRegContext ctx) {

      // TODO: probe for new devices only
      if (m_found)
	return OSVR_RETURN_SUCCESS;

      struct hid_device_info *hid_devices, *cur_dev;
      hid_devices = hid_enumerate(0x0483, 0x5750);
      if (!hid_devices)
	return OSVR_RETURN_FAILURE;

      for (cur_dev = hid_devices; cur_dev; cur_dev = cur_dev->next) {
	if (wcscmp(cur_dev->manufacturer_string, L"LYRobotix")==0 &&
	    wcscmp(cur_dev->product_string, L"NOLO")==0) {
	  /// TODO: Distinguish Headset Marker from other parts?
	  /// Create our device object
	  osvr::pluginkit::registerObjectForDeletion
	    (ctx, new NoloDevice(ctx, cur_dev->path));
	  m_found = true;
	}
      }

      hid_free_enumeration(hid_devices);
      return OSVR_RETURN_SUCCESS;
    }

  private:
    /// @brief Have we found our device yet? (this limits the plugin to one
    /// instance)
    bool m_found;
};
} // namespace

OSVR_PLUGIN(com_osvr_Nolo) {
    hid_init();
    osvr::pluginkit::PluginContext context(ctx);

    /// Register a detection callback function object.
    context.registerHardwareDetectCallback(new HardwareDetection());

    return OSVR_RETURN_SUCCESS;
}
<|endoftext|>


<commit_before>// Copyright 2014 BVLC and contributors.

#include <cstdio>

#include <algorithm>
#include <string>
#include <vector>

#include "caffe/net.hpp"
#include "caffe/feedback_net.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/solver.hpp"
#include "caffe/feedback_solver.hpp"
#include "caffe/util/io.hpp"
#include "caffe/util/math_functions.hpp"

using std::max;
using std::min;

namespace caffe {

template <typename Dtype>
FeedbackSolver<Dtype>::FeedbackSolver(const SolverParameter& param)
    : net_(), test_net_() {
  Init(param);
}

template <typename Dtype>
FeedbackSolver<Dtype>::FeedbackSolver(const string& param_file)
    : net_(), test_net_() {
  SolverParameter param;
  ReadProtoFromTextFile(param_file, &param);
  Init(param);
}

template <typename Dtype>
void FeedbackSolver<Dtype>::Init(const SolverParameter& param) {
  param_ = param;
  if (param_.random_seed() >= 0) {
    Caffe::set_random_seed(param_.random_seed());
  }
  // Scaffolding code
  LOG(INFO) << "Creating training net.";
  net_.reset(new FeedbackNet<Dtype>(param_.train_net()));
  if (param_.has_test_net()) {
    LOG(INFO) << "Creating testing net.";
    test_net_.reset(new FeedbackNet<Dtype>(param_.test_net()));
    CHECK_GT(param_.test_iter(), 0);
    CHECK_GT(param_.test_interval(), 0);
  }
  LOG(INFO) << "Solver scaffolding done.";
}

template <typename Dtype>
void FeedbackSolver<Dtype>::Solve(const char* resume_file) {
  Caffe::set_mode(Caffe::Brew(param_.solver_mode()));
  if (param_.solver_mode() == SolverParameter_SolverMode_GPU &&
      param_.has_device_id()) {
    Caffe::SetDevice(param_.device_id());
  }
  Caffe::set_phase(Caffe::TRAIN);
  LOG(INFO) << "Solving " << net_->name();
  PreSolve();

  iter_ = 0;
  resume_file = NULL;
  if (resume_file ) {
    LOG(INFO) << "Restoring previous solver status from " << resume_file;
    Restore(resume_file);
  }

  // Run a test pass before doing any training to avoid waiting a potentially
  // very long time (param_.test_interval() training iterations) to report that
  // there's not enough memory to run the test net and crash, etc.; and to gauge
  // the effect of the first training iterations.
  if (param_.test_interval()) {
    Test();
  }

  // For a network that is trained by the solver, no bottom or top vecs
  // should be given, and we will just provide dummy vecs.
  vector<Blob<Dtype>*> bottom_vec;
  while (iter_++ < param_.max_iter()) {
    Dtype loss = net_->FeedbackForwardBackward(bottom_vec, param_.top_k());
    ComputeUpdateValue();
    net_->Update();

    if (param_.display() && iter_ % param_.display() == 0) {
      LOG(INFO) << "Iteration " << iter_ << ", loss = " << loss;
    }
    if (param_.test_interval() && iter_ % param_.test_interval() == 0) {
      Test();
    }
    // Check if we need to do snapshot
    if (param_.snapshot() && iter_ % param_.snapshot() == 0) {
      Snapshot();
    }
  }
  // After the optimization is done, always do a snapshot.
  iter_--;
  Snapshot();
  LOG(INFO) << "Optimization Done.";
}

template <typename Dtype>
void FeedbackSolver<Dtype>::Test() {
  LOG(INFO) << "Iteration " << iter_ << ", Testing net";
  // We need to set phase to test before running.
  Caffe::set_phase(Caffe::TEST);
  CHECK_NOTNULL(test_net_.get())->ShareTrainedLayersWith(net_.get());
  vector<Dtype> test_score;
  vector<Blob<Dtype>*> bottom_vec;
  Dtype loss = 0;

  for (int i = 0; i < param_.test_iter(); ++i) {
	LOG(INFO)<<"Testing batch #"<<i;
    Dtype iter_loss;
    const vector<Blob<Dtype>*>& result =
      test_net_->Forward(bottom_vec, &iter_loss);
      //test_net_->FeedbackForward(bottom_vec, &iter_loss, param_.top_k());
    if (param_.test_compute_loss()) {
      loss += iter_loss;
    }
    if (i == 0) {
      for (int j = 0; j < result.size(); ++j) {
        const Dtype* result_vec = result[j]->cpu_data();
        for (int k = 0; k < result[j]->count(); ++k) {
          test_score.push_back(result_vec[k]);
        }
      }
    } else {
      int idx = 0;
      for (int j = 0; j < result.size(); ++j) {
        const Dtype* result_vec = result[j]->cpu_data();
        for (int k = 0; k < result[j]->count(); ++k) {
          test_score[idx++] += result_vec[k];
        }
      }
    }
  }
  if (param_.test_compute_loss()) {
    loss /= param_.test_iter();
    LOG(INFO) << "Test loss: " << loss;
  }
  for (int i = 0; i < test_score.size(); ++i) {
    LOG(INFO) << "Test score #" << i << ": "
        << test_score[i] / param_.test_iter();
  }
  Caffe::set_phase(Caffe::TRAIN);
}

template <typename Dtype>
void FeedbackSolver<Dtype>::Snapshot() {
  NetParameter net_param;
  // For intermediate results, we will also dump the gradient values.
  net_->ToProto(&net_param, param_.snapshot_diff());
  string filename(param_.snapshot_prefix());
  const int kBufferSize = 20;
  char iter_str_buffer[kBufferSize];
  snprintf(iter_str_buffer, kBufferSize, "_iter_%d", iter_);
  filename += iter_str_buffer;
  LOG(INFO) << "Snapshotting to " << filename;
  WriteProtoToBinaryFile(net_param, filename.c_str());
  SolverState state;
  SnapshotSolverState(&state);
  state.set_iter(iter_);
  state.set_learned_net(filename);
  filename += ".solverstate";
  LOG(INFO) << "Snapshotting solver state to " << filename;
  WriteProtoToBinaryFile(state, filename.c_str());
}

template <typename Dtype>
void FeedbackSolver<Dtype>::Restore(const char* state_file) {
  SolverState state;
  NetParameter net_param;
  ReadProtoFromBinaryFile(state_file, &state);
  if (state.has_learned_net()) {
	  LOG(INFO)<<"Copy trained model from "<<state.learned_net();
    ReadProtoFromBinaryFile(state.learned_net().c_str(), &net_param);
    net_->CopyTrainedLayersFrom(net_param);
  }
  iter_ = state.iter();
  RestoreSolverState(state);
}

// Return the current learning rate. The currently implemented learning rate
// policies are as follows:
//    - fixed: always return base_lr.
//    - step: return base_lr * gamma ^ (floor(iter / step))
//    - exp: return base_lr * gamma ^ iter
//    - inv: return base_lr * (1 + gamma * iter) ^ (- power)
// where base_lr, gamma, step and power are defined in the solver parameter
// protocol buffer, and iter is the current iteration.
template <typename Dtype>
Dtype SGDFeedbackSolver<Dtype>::GetLearningRate() {
  Dtype rate;
  const string& lr_policy = this->param_.lr_policy();
  if (lr_policy == "fixed") {
    rate = this->param_.base_lr();
  } else if (lr_policy == "step") {
    int current_step = this->iter_ / this->param_.stepsize();
    rate = this->param_.base_lr() *
        pow(this->param_.gamma(), current_step);
  } else if (lr_policy == "exp") {
    rate = this->param_.base_lr() * pow(this->param_.gamma(), this->iter_);
  } else if (lr_policy == "inv") {
    rate = this->param_.base_lr() *
        pow(Dtype(1) + this->param_.gamma() * this->iter_,
            - this->param_.power());
  } else {
    LOG(FATAL) << "Unknown learning rate policy: " << lr_policy;
  }
  return rate;
}

template <typename Dtype>
void SGDFeedbackSolver<Dtype>::PreSolve() {
  // Initialize the history
  vector<shared_ptr<Blob<Dtype> > >& net_params = this->net_->params();
  history_.clear();
  for (int i = 0; i < net_params.size(); ++i) {
    const Blob<Dtype>* net_param = net_params[i].get();
    history_.push_back(shared_ptr<Blob<Dtype> >(new Blob<Dtype>(
        net_param->num(), net_param->channels(), net_param->height(),
        net_param->width())));
  }
}

template <typename Dtype>
void SGDFeedbackSolver<Dtype>::ComputeUpdateValue() {
  vector<shared_ptr<Blob<Dtype> > >& net_params = this->net_->params();
  vector<float>& net_params_lr = this->net_->params_lr();
  vector<float>& net_params_weight_decay = this->net_->params_weight_decay();
  // get the learning rate
  Dtype rate = GetLearningRate();
  if (this->param_.display() && this->iter_ % this->param_.display() == 0) {
    LOG(INFO) << "Iteration " << this->iter_ << ", lr = " << rate;
  }
  Dtype momentum = this->param_.momentum();
  Dtype weight_decay = this->param_.weight_decay();
  switch (Caffe::mode()) {
  case Caffe::CPU:
    for (int param_id = 0; param_id < net_params.size(); ++param_id) {
      // Compute the value to history, and then copy them to the blob's diff.
      Dtype local_rate = rate * net_params_lr[param_id];
      Dtype local_decay = weight_decay * net_params_weight_decay[param_id];
      caffe_cpu_axpby(net_params[param_id]->count(), local_rate,
          net_params[param_id]->cpu_diff(), momentum,
          history_[param_id]->mutable_cpu_data());
      if (local_decay) {
        // add weight decay
        caffe_axpy(net_params[param_id]->count(),
            local_decay * local_rate,
            net_params[param_id]->cpu_data(),
            history_[param_id]->mutable_cpu_data());
      }
      // copy
      caffe_copy(net_params[param_id]->count(),
          history_[param_id]->cpu_data(),
          net_params[param_id]->mutable_cpu_diff());
    }
    break;
  case Caffe::GPU:
    for (int param_id = 0; param_id < net_params.size(); ++param_id) {
      // Compute the value to history, and then copy them to the blob's diff.
      Dtype local_rate = rate * net_params_lr[param_id];
      Dtype local_decay = weight_decay * net_params_weight_decay[param_id];
      caffe_gpu_axpby(net_params[param_id]->count(), local_rate,
          net_params[param_id]->gpu_diff(), momentum,
          history_[param_id]->mutable_gpu_data());
      if (local_decay) {
        // add weight decay
        caffe_gpu_axpy(net_params[param_id]->count(),
            local_decay * local_rate,
            net_params[param_id]->gpu_data(),
            history_[param_id]->mutable_gpu_data());
      }
      // copy
      caffe_gpu_copy(net_params[param_id]->count(),
          history_[param_id]->gpu_data(),
          net_params[param_id]->mutable_gpu_diff());
    }
    break;
  default:
    LOG(FATAL) << "Unknown caffe mode: " << Caffe::mode();
  }
}

template <typename Dtype>
void SGDFeedbackSolver<Dtype>::SnapshotSolverState(SolverState* state) {
  state->clear_history();
  for (int i = 0; i < history_.size(); ++i) {
    // Add history
    BlobProto* history_blob = state->add_history();
    history_[i]->ToProto(history_blob);
  }
}

template <typename Dtype>
void SGDFeedbackSolver<Dtype>::RestoreSolverState(const SolverState& state) {
  CHECK_EQ(state.history_size(), history_.size())
      << "Incorrect length of history blobs.";
  LOG(INFO) << "SGDSolver: restoring history";
  for (int i = 0; i < history_.size(); ++i) {
    history_[i]->FromProto(state.history(i));
  }
}

INSTANTIATE_CLASS(FeedbackSolver);
INSTANTIATE_CLASS(SGDFeedbackSolver);

}  // namespace caffe
<commit_msg>Modification: feedback_solver::test()<commit_after>// Copyright 2014 BVLC and contributors.

#include <cstdio>

#include <algorithm>
#include <string>
#include <vector>

#include "caffe/net.hpp"
#include "caffe/feedback_net.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/solver.hpp"
#include "caffe/feedback_solver.hpp"
#include "caffe/util/io.hpp"
#include "caffe/util/math_functions.hpp"

using std::max;
using std::min;

namespace caffe {

template <typename Dtype>
FeedbackSolver<Dtype>::FeedbackSolver(const SolverParameter& param)
    : net_(), test_net_() {
  Init(param);
}

template <typename Dtype>
FeedbackSolver<Dtype>::FeedbackSolver(const string& param_file)
    : net_(), test_net_() {
  SolverParameter param;
  ReadProtoFromTextFile(param_file, &param);
  Init(param);
}

template <typename Dtype>
void FeedbackSolver<Dtype>::Init(const SolverParameter& param) {
  param_ = param;
  if (param_.random_seed() >= 0) {
    Caffe::set_random_seed(param_.random_seed());
  }
  // Scaffolding code
  LOG(INFO) << "Creating training net.";
  net_.reset(new FeedbackNet<Dtype>(param_.train_net()));
  if (param_.has_test_net()) {
    LOG(INFO) << "Creating testing net.";
    test_net_.reset(new FeedbackNet<Dtype>(param_.test_net()));
    CHECK_GT(param_.test_iter(), 0);
    CHECK_GT(param_.test_interval(), 0);
  }
  LOG(INFO) << "Solver scaffolding done.";
}

template <typename Dtype>
void FeedbackSolver<Dtype>::Solve(const char* resume_file) {
  Caffe::set_mode(Caffe::Brew(param_.solver_mode()));
  if (param_.solver_mode() == SolverParameter_SolverMode_GPU &&
      param_.has_device_id()) {
    Caffe::SetDevice(param_.device_id());
  }
  Caffe::set_phase(Caffe::TRAIN);
  LOG(INFO) << "Solving " << net_->name();
  PreSolve();

  iter_ = 0;
  resume_file = NULL;
  if (resume_file ) {
    LOG(INFO) << "Restoring previous solver status from " << resume_file;
    Restore(resume_file);
  }

  // Run a test pass before doing any training to avoid waiting a potentially
  // very long time (param_.test_interval() training iterations) to report that
  // there's not enough memory to run the test net and crash, etc.; and to gauge
  // the effect of the first training iterations.
  if (param_.test_interval()) {
    Test();
  }

  // For a network that is trained by the solver, no bottom or top vecs
  // should be given, and we will just provide dummy vecs.
  vector<Blob<Dtype>*> bottom_vec;
  while (iter_++ < param_.max_iter()) {
    Dtype loss = net_->FeedbackForwardBackward(bottom_vec, param_.top_k());
    ComputeUpdateValue();
    net_->Update();

    if (param_.display() && iter_ % param_.display() == 0) {
      LOG(INFO) << "Iteration " << iter_ << ", loss = " << loss;
    }
    if (param_.test_interval() && iter_ % param_.test_interval() == 0) {
      Test();
    }
    // Check if we need to do snapshot
    if (param_.snapshot() && iter_ % param_.snapshot() == 0) {
      Snapshot();
    }
  }
  // After the optimization is done, always do a snapshot.
  iter_--;
  Snapshot();
  LOG(INFO) << "Optimization Done.";
}

template <typename Dtype>
void FeedbackSolver<Dtype>::Test() {
  LOG(INFO) << "Iteration " << iter_ << ", Testing net";
  // We need to set phase to test before running.
  Caffe::set_phase(Caffe::TEST);
  CHECK_NOTNULL(test_net_.get())->ShareTrainedLayersWith(net_.get());
  vector<Dtype> test_score;
  vector<Blob<Dtype>*> bottom_vec;
  Dtype loss = 0;

  for (int i = 0; i < param_.test_iter(); ++i) {
    Dtype iter_loss;
    const vector<Blob<Dtype>*>& result =
      test_net_->Forward(bottom_vec, &iter_loss);
      //test_net_->FeedbackForward(bottom_vec, &iter_loss, param_.top_k());
    if (param_.test_compute_loss()) {
      loss += iter_loss;
    }
    if (i == 0) {
      for (int j = 0; j < result.size(); ++j) {
        const Dtype* result_vec = result[j]->cpu_data();
        for (int k = 0; k < result[j]->count(); ++k) {
          test_score.push_back(result_vec[k]);
        }
      }
    } else {
      int idx = 0;
      for (int j = 0; j < result.size(); ++j) {
        const Dtype* result_vec = result[j]->cpu_data();
        for (int k = 0; k < result[j]->count(); ++k) {
          test_score[idx++] += result_vec[k];
        }
      }
    }
  }
  if (param_.test_compute_loss()) {
    loss /= param_.test_iter();
    LOG(INFO) << "Test loss: " << loss;
  }
  for (int i = 0; i < test_score.size(); ++i) {
    LOG(INFO) << "Test score #" << i << ": "
        << test_score[i] / param_.test_iter();
  }
  Caffe::set_phase(Caffe::TRAIN);
}

template <typename Dtype>
void FeedbackSolver<Dtype>::Snapshot() {
  NetParameter net_param;
  // For intermediate results, we will also dump the gradient values.
  net_->ToProto(&net_param, param_.snapshot_diff());
  string filename(param_.snapshot_prefix());
  const int kBufferSize = 20;
  char iter_str_buffer[kBufferSize];
  snprintf(iter_str_buffer, kBufferSize, "_iter_%d", iter_);
  filename += iter_str_buffer;
  LOG(INFO) << "Snapshotting to " << filename;
  WriteProtoToBinaryFile(net_param, filename.c_str());
  SolverState state;
  SnapshotSolverState(&state);
  state.set_iter(iter_);
  state.set_learned_net(filename);
  filename += ".solverstate";
  LOG(INFO) << "Snapshotting solver state to " << filename;
  WriteProtoToBinaryFile(state, filename.c_str());
}

template <typename Dtype>
void FeedbackSolver<Dtype>::Restore(const char* state_file) {
  SolverState state;
  NetParameter net_param;
  ReadProtoFromBinaryFile(state_file, &state);
  if (state.has_learned_net()) {
	  LOG(INFO)<<"Copy trained model from "<<state.learned_net();
    ReadProtoFromBinaryFile(state.learned_net().c_str(), &net_param);
    net_->CopyTrainedLayersFrom(net_param);
  }
  iter_ = state.iter();
  RestoreSolverState(state);
}

// Return the current learning rate. The currently implemented learning rate
// policies are as follows:
//    - fixed: always return base_lr.
//    - step: return base_lr * gamma ^ (floor(iter / step))
//    - exp: return base_lr * gamma ^ iter
//    - inv: return base_lr * (1 + gamma * iter) ^ (- power)
// where base_lr, gamma, step and power are defined in the solver parameter
// protocol buffer, and iter is the current iteration.
template <typename Dtype>
Dtype SGDFeedbackSolver<Dtype>::GetLearningRate() {
  Dtype rate;
  const string& lr_policy = this->param_.lr_policy();
  if (lr_policy == "fixed") {
    rate = this->param_.base_lr();
  } else if (lr_policy == "step") {
    int current_step = this->iter_ / this->param_.stepsize();
    rate = this->param_.base_lr() *
        pow(this->param_.gamma(), current_step);
  } else if (lr_policy == "exp") {
    rate = this->param_.base_lr() * pow(this->param_.gamma(), this->iter_);
  } else if (lr_policy == "inv") {
    rate = this->param_.base_lr() *
        pow(Dtype(1) + this->param_.gamma() * this->iter_,
            - this->param_.power());
  } else {
    LOG(FATAL) << "Unknown learning rate policy: " << lr_policy;
  }
  return rate;
}

template <typename Dtype>
void SGDFeedbackSolver<Dtype>::PreSolve() {
  // Initialize the history
  vector<shared_ptr<Blob<Dtype> > >& net_params = this->net_->params();
  history_.clear();
  for (int i = 0; i < net_params.size(); ++i) {
    const Blob<Dtype>* net_param = net_params[i].get();
    history_.push_back(shared_ptr<Blob<Dtype> >(new Blob<Dtype>(
        net_param->num(), net_param->channels(), net_param->height(),
        net_param->width())));
  }
}

template <typename Dtype>
void SGDFeedbackSolver<Dtype>::ComputeUpdateValue() {
  vector<shared_ptr<Blob<Dtype> > >& net_params = this->net_->params();
  vector<float>& net_params_lr = this->net_->params_lr();
  vector<float>& net_params_weight_decay = this->net_->params_weight_decay();
  // get the learning rate
  Dtype rate = GetLearningRate();
  if (this->param_.display() && this->iter_ % this->param_.display() == 0) {
    LOG(INFO) << "Iteration " << this->iter_ << ", lr = " << rate;
  }
  Dtype momentum = this->param_.momentum();
  Dtype weight_decay = this->param_.weight_decay();
  switch (Caffe::mode()) {
  case Caffe::CPU:
    for (int param_id = 0; param_id < net_params.size(); ++param_id) {
      // Compute the value to history, and then copy them to the blob's diff.
      Dtype local_rate = rate * net_params_lr[param_id];
      Dtype local_decay = weight_decay * net_params_weight_decay[param_id];
      caffe_cpu_axpby(net_params[param_id]->count(), local_rate,
          net_params[param_id]->cpu_diff(), momentum,
          history_[param_id]->mutable_cpu_data());
      if (local_decay) {
        // add weight decay
        caffe_axpy(net_params[param_id]->count(),
            local_decay * local_rate,
            net_params[param_id]->cpu_data(),
            history_[param_id]->mutable_cpu_data());
      }
      // copy
      caffe_copy(net_params[param_id]->count(),
          history_[param_id]->cpu_data(),
          net_params[param_id]->mutable_cpu_diff());
    }
    break;
  case Caffe::GPU:
    for (int param_id = 0; param_id < net_params.size(); ++param_id) {
      // Compute the value to history, and then copy them to the blob's diff.
      Dtype local_rate = rate * net_params_lr[param_id];
      Dtype local_decay = weight_decay * net_params_weight_decay[param_id];
      caffe_gpu_axpby(net_params[param_id]->count(), local_rate,
          net_params[param_id]->gpu_diff(), momentum,
          history_[param_id]->mutable_gpu_data());
      if (local_decay) {
        // add weight decay
        caffe_gpu_axpy(net_params[param_id]->count(),
            local_decay * local_rate,
            net_params[param_id]->gpu_data(),
            history_[param_id]->mutable_gpu_data());
      }
      // copy
      caffe_gpu_copy(net_params[param_id]->count(),
          history_[param_id]->gpu_data(),
          net_params[param_id]->mutable_gpu_diff());
    }
    break;
  default:
    LOG(FATAL) << "Unknown caffe mode: " << Caffe::mode();
  }
}

template <typename Dtype>
void SGDFeedbackSolver<Dtype>::SnapshotSolverState(SolverState* state) {
  state->clear_history();
  for (int i = 0; i < history_.size(); ++i) {
    // Add history
    BlobProto* history_blob = state->add_history();
    history_[i]->ToProto(history_blob);
  }
}

template <typename Dtype>
void SGDFeedbackSolver<Dtype>::RestoreSolverState(const SolverState& state) {
  CHECK_EQ(state.history_size(), history_.size())
      << "Incorrect length of history blobs.";
  LOG(INFO) << "SGDSolver: restoring history";
  for (int i = 0; i < history_.size(); ++i) {
    history_[i]->FromProto(state.history(i));
  }
}

INSTANTIATE_CLASS(FeedbackSolver);
INSTANTIATE_CLASS(SGDFeedbackSolver);

}  // namespace caffe
<|endoftext|>


<commit_before>/*
 * Main Send File Transfer Window
 *
 * Copyright (C) 2011 David Edmundson <kde@davidedmundson.co.uk>
 *
 * 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <KFileItem>
#include <KApplication>
#include <KMimeType>
#include <KDebug>
#include <KMessageBox>
#include <KPixmapSequence>
#include <KPixmapSequenceOverlayPainter>
#include <KLineEdit>
#include <KIO/PreviewJob>

#include <QtGui/QPushButton>

#include <TelepathyQt/AccountManager>
#include <TelepathyQt/PendingChannelRequest>
#include <TelepathyQt/PendingReady>

#include <KTp/Models/contacts-model.h>
#include <KTp/Models/accounts-filter-model.h>
#include <KTp/Widgets/contact-grid-widget.h>

//FIXME, copy and paste the approver code for loading this from a config file into this, the contact list and the chat handler.
#define PREFERRED_FILETRANSFER_HANDLER "org.freedesktop.Telepathy.Client.KTp.FileTransfer"

MainWindow::MainWindow(const KUrl &url, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MainWindow),
    m_url(url),
    m_contactsModel(0)
{
    Tp::registerTypes();

    ui->setupUi(this);
    setWindowTitle(i18n("Send file - %1", url.fileName()));

    kDebug() << KApplication::arguments();

    ui->fileNameLabel->setText(url.fileName());
    m_busyOverlay = new KPixmapSequenceOverlayPainter(this);
    m_busyOverlay->setSequence(KPixmapSequence("process-working", 22));
    m_busyOverlay->setWidget(ui->filePreview);
    m_busyOverlay->start();

    KFileItem file(KFileItem::Unknown, KFileItem::Unknown, url);
    QStringList availablePlugins = KIO::PreviewJob::availablePlugins();
    KIO::PreviewJob* job = KIO::filePreview(KFileItemList() << file, QSize(280, 280), &availablePlugins);
    job->setOverlayIconAlpha(0);
    job->setScaleType(KIO::PreviewJob::Unscaled);
    connect(job, SIGNAL(gotPreview(KFileItem,QPixmap)),
            this, SLOT(onPreviewLoaded(KFileItem,QPixmap)));
    connect(job, SIGNAL(failed(KFileItem)),
            this, SLOT(onPreviewFailed(KFileItem)));

    Tp::AccountFactoryPtr  accountFactory = Tp::AccountFactory::create(QDBusConnection::sessionBus(),
                                                                       Tp::Features() << Tp::Account::FeatureCore
                                                                       << Tp::Account::FeatureAvatar
                                                                       << Tp::Account::FeatureProtocolInfo
                                                                       << Tp::Account::FeatureProfile
                                                                       << Tp::Account::FeatureCapabilities);

    Tp::ConnectionFactoryPtr connectionFactory = Tp::ConnectionFactory::create(QDBusConnection::sessionBus(),
                                                                               Tp::Features() << Tp::Connection::FeatureCore
                                                                               << Tp::Connection::FeatureRosterGroups
                                                                               << Tp::Connection::FeatureRoster
                                                                               << Tp::Connection::FeatureSelfContact);

    Tp::ContactFactoryPtr contactFactory = Tp::ContactFactory::create(Tp::Features()  << Tp::Contact::FeatureAlias
                                                                      << Tp::Contact::FeatureAvatarData
                                                                      << Tp::Contact::FeatureSimplePresence
                                                                      << Tp::Contact::FeatureCapabilities);

    Tp::ChannelFactoryPtr channelFactory = Tp::ChannelFactory::create(QDBusConnection::sessionBus());

    m_accountManager = Tp::AccountManager::create(QDBusConnection::sessionBus(),
                                                  accountFactory,
                                                  connectionFactory,
                                                  channelFactory,
                                                  contactFactory);

    m_contactsModel = new ContactsModel(this);
    connect(m_accountManager->becomeReady(), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onAccountManagerReady()));

    m_contactGridWidget = new KTp::ContactGridWidget(m_contactsModel, this);
    m_contactGridWidget->contactFilterLineEdit()->setClickMessage(i18n("Search in Contacts..."));
    m_contactGridWidget->filter()->setPresenceTypeFilterFlags(AccountsFilterModel::ShowOnlyConnected);
    m_contactGridWidget->filter()->setCapabilityFilterFlags(AccountsFilterModel::FilterByFileTransferCapability);
    ui->recipientVLayout->addWidget(m_contactGridWidget);

    connect(m_contactGridWidget,
            SIGNAL(selectionChanged(Tp::AccountPtr,Tp::ContactPtr)),
            SLOT(onContactSelectionChanged(Tp::AccountPtr,Tp::ContactPtr)));

    ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true);
    connect(ui->buttonBox, SIGNAL(accepted()), SLOT(onDialogAccepted()));
    connect(ui->buttonBox, SIGNAL(rejected()), SLOT(close()));
}

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

void MainWindow::onAccountManagerReady()
{
    m_contactsModel->setAccountManager(m_accountManager);
}

void MainWindow::onDialogAccepted()
{
    // don't do anytghing if no contact has been selected
    if (!m_contactGridWidget->hasSelection()) {
        // show message box?
        return;
    }

    Tp::ContactPtr contact = m_contactGridWidget->selectedContact();
    Tp::AccountPtr sendingAccount = m_contactGridWidget->selectedAccount();

    if (sendingAccount.isNull()) {
        kDebug() << "sending account: NULL";
    } else {
        kDebug() << "Account is: " << sendingAccount->displayName();
        kDebug() << "sending to: " << contact->alias();
    }

    // start sending file
    kDebug() << "FILE TO SEND: " << m_url.path();
    Tp::FileTransferChannelCreationProperties fileTransferProperties(m_url.path(), KMimeType::findByFileContent(m_url.path())->name());

    Tp::PendingChannelRequest* channelRequest = sendingAccount->createFileTransfer(contact,
                                                                                fileTransferProperties,
                                                                                QDateTime::currentDateTime(),
                                                                                PREFERRED_FILETRANSFER_HANDLER);

    connect(channelRequest, SIGNAL(finished(Tp::PendingOperation*)), SLOT(slotFileTransferFinished(Tp::PendingOperation*)));

    //disable the buttons
    foreach(QAbstractButton* button, ui->buttonBox->buttons()) {
        button->setEnabled(false);
    }
}

void MainWindow::onContactSelectionChanged(Tp::AccountPtr account, Tp::ContactPtr contact)
{
    Q_UNUSED(account)
    Q_UNUSED(contact)

    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_contactGridWidget->hasSelection());
}

void MainWindow::slotFileTransferFinished(Tp::PendingOperation* op)
{
    if (op->isError()) {
        //FIXME map to human readable strings.
        QString errorMsg(op->errorName() + ": " + op->errorMessage());
        kDebug() << "ERROR!: " << errorMsg;
        KMessageBox::error(this, i18n("Failed to send file"), i18n("File Transfer Failed"));
        close();
    } else {
        kDebug() << "Transfer started";
        // now I can close the dialog
        close();
    }
}

void MainWindow::onPreviewLoaded(const KFileItem& item, const QPixmap& preview)
{
    Q_UNUSED(item);
    ui->filePreview->setPixmap(preview);
    m_busyOverlay->stop();
}

void MainWindow::onPreviewFailed(const KFileItem& item)
{
    kWarning() << "Loading thumb failed" << item.name();
    ui->filePreview->setPixmap(KIconLoader::global()->loadIcon(item.iconName(), KIconLoader::Desktop, 128));
    m_busyOverlay->stop();
}
<commit_msg>Port to use KTp::Actions<commit_after>/*
 * Main Send File Transfer Window
 *
 * Copyright (C) 2011 David Edmundson <kde@davidedmundson.co.uk>
 *
 * 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.1 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <KFileItem>
#include <KApplication>
#include <KMimeType>
#include <KDebug>
#include <KMessageBox>
#include <KPixmapSequence>
#include <KPixmapSequenceOverlayPainter>
#include <KLineEdit>
#include <KIO/PreviewJob>

#include <QtGui/QPushButton>

#include <TelepathyQt/AccountManager>
#include <TelepathyQt/PendingChannelRequest>
#include <TelepathyQt/PendingReady>

#include <KTp/actions.h>
#include <KTp/Models/contacts-model.h>
#include <KTp/Models/accounts-filter-model.h>
#include <KTp/Widgets/contact-grid-widget.h>

MainWindow::MainWindow(const KUrl &url, QWidget *parent) :
    QWidget(parent),
    ui(new Ui::MainWindow),
    m_url(url),
    m_contactsModel(0)
{
    Tp::registerTypes();

    ui->setupUi(this);
    setWindowTitle(i18n("Send file - %1", url.fileName()));

    kDebug() << KApplication::arguments();

    ui->fileNameLabel->setText(url.fileName());
    m_busyOverlay = new KPixmapSequenceOverlayPainter(this);
    m_busyOverlay->setSequence(KPixmapSequence("process-working", 22));
    m_busyOverlay->setWidget(ui->filePreview);
    m_busyOverlay->start();

    KFileItem file(KFileItem::Unknown, KFileItem::Unknown, url);
    QStringList availablePlugins = KIO::PreviewJob::availablePlugins();
    KIO::PreviewJob* job = KIO::filePreview(KFileItemList() << file, QSize(280, 280), &availablePlugins);
    job->setOverlayIconAlpha(0);
    job->setScaleType(KIO::PreviewJob::Unscaled);
    connect(job, SIGNAL(gotPreview(KFileItem,QPixmap)),
            this, SLOT(onPreviewLoaded(KFileItem,QPixmap)));
    connect(job, SIGNAL(failed(KFileItem)),
            this, SLOT(onPreviewFailed(KFileItem)));

    Tp::AccountFactoryPtr  accountFactory = Tp::AccountFactory::create(QDBusConnection::sessionBus(),
                                                                       Tp::Features() << Tp::Account::FeatureCore
                                                                       << Tp::Account::FeatureAvatar
                                                                       << Tp::Account::FeatureProtocolInfo
                                                                       << Tp::Account::FeatureProfile
                                                                       << Tp::Account::FeatureCapabilities);

    Tp::ConnectionFactoryPtr connectionFactory = Tp::ConnectionFactory::create(QDBusConnection::sessionBus(),
                                                                               Tp::Features() << Tp::Connection::FeatureCore
                                                                               << Tp::Connection::FeatureRosterGroups
                                                                               << Tp::Connection::FeatureRoster
                                                                               << Tp::Connection::FeatureSelfContact);

    Tp::ContactFactoryPtr contactFactory = Tp::ContactFactory::create(Tp::Features()  << Tp::Contact::FeatureAlias
                                                                      << Tp::Contact::FeatureAvatarData
                                                                      << Tp::Contact::FeatureSimplePresence
                                                                      << Tp::Contact::FeatureCapabilities);

    Tp::ChannelFactoryPtr channelFactory = Tp::ChannelFactory::create(QDBusConnection::sessionBus());

    m_accountManager = Tp::AccountManager::create(QDBusConnection::sessionBus(),
                                                  accountFactory,
                                                  connectionFactory,
                                                  channelFactory,
                                                  contactFactory);

    m_contactsModel = new ContactsModel(this);
    connect(m_accountManager->becomeReady(), SIGNAL(finished(Tp::PendingOperation*)), SLOT(onAccountManagerReady()));

    m_contactGridWidget = new KTp::ContactGridWidget(m_contactsModel, this);
    m_contactGridWidget->contactFilterLineEdit()->setClickMessage(i18n("Search in Contacts..."));
    m_contactGridWidget->filter()->setPresenceTypeFilterFlags(AccountsFilterModel::ShowOnlyConnected);
    m_contactGridWidget->filter()->setCapabilityFilterFlags(AccountsFilterModel::FilterByFileTransferCapability);
    ui->recipientVLayout->addWidget(m_contactGridWidget);

    connect(m_contactGridWidget,
            SIGNAL(selectionChanged(Tp::AccountPtr,Tp::ContactPtr)),
            SLOT(onContactSelectionChanged(Tp::AccountPtr,Tp::ContactPtr)));

    ui->buttonBox->button(QDialogButtonBox::Ok)->setDisabled(true);
    connect(ui->buttonBox, SIGNAL(accepted()), SLOT(onDialogAccepted()));
    connect(ui->buttonBox, SIGNAL(rejected()), SLOT(close()));
}

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

void MainWindow::onAccountManagerReady()
{
    m_contactsModel->setAccountManager(m_accountManager);
}

void MainWindow::onDialogAccepted()
{
    // don't do anytghing if no contact has been selected
    if (!m_contactGridWidget->hasSelection()) {
        // show message box?
        return;
    }

    // start sending file
    Tp::PendingChannelRequest* channelRequest = KTp::Actions::startFileTransfer(m_contactGridWidget->selectedAccount(),
                                                                                m_contactGridWidget->selectedContact(),
                                                                                m_url.path());

    connect(channelRequest, SIGNAL(finished(Tp::PendingOperation*)), SLOT(slotFileTransferFinished(Tp::PendingOperation*)));

    //disable the buttons
    foreach(QAbstractButton* button, ui->buttonBox->buttons()) {
        button->setEnabled(false);
    }
}

void MainWindow::onContactSelectionChanged(Tp::AccountPtr account, Tp::ContactPtr contact)
{
    Q_UNUSED(account)
    Q_UNUSED(contact)

    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_contactGridWidget->hasSelection());
}

void MainWindow::slotFileTransferFinished(Tp::PendingOperation* op)
{
    if (op->isError()) {
        //FIXME map to human readable strings.
        QString errorMsg(op->errorName() + ": " + op->errorMessage());
        kDebug() << "ERROR!: " << errorMsg;
        KMessageBox::error(this, i18n("Failed to send file"), i18n("File Transfer Failed"));
        close();
    } else {
        kDebug() << "Transfer started";
        // now I can close the dialog
        close();
    }
}

void MainWindow::onPreviewLoaded(const KFileItem& item, const QPixmap& preview)
{
    Q_UNUSED(item);
    ui->filePreview->setPixmap(preview);
    m_busyOverlay->stop();
}

void MainWindow::onPreviewFailed(const KFileItem& item)
{
    kWarning() << "Loading thumb failed" << item.name();
    ui->filePreview->setPixmap(KIconLoader::global()->loadIcon(item.iconName(), KIconLoader::Desktop, 128));
    m_busyOverlay->stop();
}
<|endoftext|>