<commit_before>//
// LibSourcey
// Copyright (C) 2005, Sourcey <http://sourcey.com>
//
// LibSourcey 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.
//
// LibSourcey 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

#include "scy/turn/client/tcpclient.h"
#include "scy/logger.h"
#include "scy/net/tcpsocket.h"
//#include "Poco/Format.h"

#include <assert.h>
#include <iostream>
#include <algorithm>

using namespace std;

namespace scy {
namespace turn {

TCPClient::TCPClient(TCPClientObserver& observer, const Client::Options& options) :
	Client(observer, options),
	_observer(observer)
{
	TraceL << "Create" << endl;

	_socket = net::makeSocket<net::TCPSocket>();
	//std::make_shared<net::TCPSocket>();
	//_socket.assign(new net::TCPSocket, false);
}

TCPClient::~TCPClient()
{
	TraceL << "Destroy" << endl;
	shutdown();
	//assert(connections().empty());
}

void TCPClient::initiate()
{
	Client::initiate();
}

void TCPClient::shutdown()
{
	TraceL << "Shutdown" << endl;

	//if (closed()) {
	//	TraceL << "Already closed" << endl;
	//	return;
	//}

	// Destroy transactions and stop timer
	Client::shutdown();

	{
		//Mutex::ScopedLock lock(_mutex);
		auto connections = _connections.map();
		TraceL << "Shutdown: Active connections: " << connections.size() << endl;
		for (auto it = connections.begin(); it != connections.end(); ++it) {
			//assert(it->second->base().refCount() == 1);

			// The connection will be removed via onRelayConnectionClosed
			it->second->close();
		}
		//assert(connections().empty());
	}

	TraceL << "Shutdown: OK" << endl;
}

void TCPClient::sendConnectRequest(const net::Address& peerAddress)
{
	// 4.3. Initiating a Connection
	//
	// To initiate a TCP connection to a peer, a client MUST send a Connect
	// request over the control connection for the desired allocation.  The
	// Connect request MUST include an XOR-PEER-ADDRESS attribute containing
	// the transport address of the peer to which a connection is desired.

	TraceL << "Send Connect request" << endl;

	auto transaction = createTransaction();
	//transaction->request().setType(stun::Message::Connect);
	//stun::Message request(stun::Message::Request, stun::Message::Allocate);
	transaction->request().setClass(stun::Message::Request);
	transaction->request().setMethod(stun::Message::Connect);

	auto peerAttr = new stun::XorPeerAddress;
	peerAttr->setAddress(peerAddress);
	//peerAttr->setFamily(1);
	//peerAttr->setPort(peerAddress.port());
	//peerAttr->setIP(peerAddress.host());
	transaction->request().add(peerAttr);

	sendAuthenticatedTransaction(transaction);
}

void TCPClient::sendData(const char* data, std::size_t size, const net::Address& peerAddress)
{
	TraceL << "Send data to " << peerAddress << endl;

	// Ensure permissions exist for the peer.
	if (!hasPermission(peerAddress.host()))
		throw std::runtime_error("No permission exists for peer: " + peerAddress.host());

	auto conn = connections().get(peerAddress, false);
	if (!conn)
		throw std::runtime_error("No peer exists for: " + peerAddress.toString());

	conn->send(data, size);
}

bool TCPClient::handleResponse(const stun::Message& response)
{
	if (!Client::handleResponse(response)) {
		if (response.methodType() ==  stun::Message::Connect &&
			response.classType() == stun::Message::SuccessResponse)
			handleConnectResponse(response);

		else if (response.methodType() ==  stun::Message::ConnectionAttempt)
			handleConnectionAttemptIndication(response);

		else if (response.methodType() ==  stun::Message::ConnectionBind &&
			response.classType() == stun::Message::SuccessResponse)
			handleConnectionBindResponse(response);

		else if (response.methodType() ==  stun::Message::ConnectionBind &&
			response.classType() == stun::Message::ErrorResponse)
			handleConnectionBindErrorResponse(response);

		else if (response.methodType() ==  stun::Message::Connect &&
			response.classType() == stun::Message::ErrorResponse)
			handleConnectErrorResponse(response);

		else
			return false;
	}

	return true;
}

void TCPClient::handleConnectResponse(const stun::Message& response)
{
	// If the connection is successfully established, the client will
	// receive a success response.  That response will contain a
	// CONNECTION-ID attribute.  The client MUST initiate a new TCP
	// connection to the server, utilizing the same destination transport
	// address to which the control connection was established.  This
	// connection MUST be made using a different local transport address.
	// Authentication of the client by the server MUST use the same method
	// and credentials as for the control connection.  Once established, the
	// client MUST send a ConnectionBind request over the new connection.
	// That request MUST include the CONNECTION-ID attribute, echoed from
	// the Connect Success response.  When a response to the ConnectionBind
	// request is received, if it is a success, the TCP connection on which
	// it was sent is called the client data connection corresponding to the
	// peer.
	//

	auto transaction = reinterpret_cast<stun::Transaction*>(response.opaque);
	auto peerAttr = transaction->request().get<stun::XorPeerAddress>();
	if (!peerAttr || (peerAttr && peerAttr->family() != 1)) {
		assert(0);
		return;
	}

	auto connAttr = response.get<stun::ConnectionID>();
	if (!connAttr) {
		assert(0);
		return;
	}

	createAndBindConnection(connAttr->value(), peerAttr->address());
}

void TCPClient::handleConnectErrorResponse(const stun::Message& response)
{
	// If the result of the Connect request was an Error Response, and the
	// response code was 447 (Connection Timeout or Failure), it means that
	// the TURN server was unable to connect to the peer.  The client MAY
	// retry with the same XOR-PEER-ADDRESS attribute, but MUST wait at
	// least 10 seconds.
	//
	// As with any other request, multiple Connect requests MAY be sent
	// simultaneously.  However, Connect requests with the same XOR-PEER-
	// ADDRESS parameter MUST NOT be sent simultaneously.

	auto transaction = reinterpret_cast<stun::Transaction*>(response.opaque);
	auto peerAttr = transaction->request().get<stun::XorPeerAddress>();
	if (!peerAttr || (peerAttr && peerAttr->family() != 1)) {
		assert(0);
		return;
	}

	_observer.onRelayConnectionBindingFailed(*this, peerAttr->address());
}

void TCPClient::handleConnectionAttemptIndication(const stun::Message& response)
{
	// 4.4. Receiving a Connection
	//
	// After an Allocate request is successfully processed by the server,
	// the client will start receiving a ConnectionAttempt indication each
	// time a peer for which a permission has been installed attempts a new
	// connection to the relayed transport address.  This indication will
	// contain CONNECTION-ID and XOR-PEER-ADDRESS attributes.  If the client
	// wishes to accept this connection, it MUST initiate a new TCP
	// connection to the server, utilizing the same destination transport
	// address to which the control connection was established.  This
	// connection MUST be made using a different local transport address.
	// Authentication of the client by the server MUST use the same method
	// and credentials as for the control connection.  Once established, the
	// client MUST send a ConnectionBind request over the new connection.
	// That request MUST include the CONNECTION-ID attribute, echoed from
	// the ConnectionAttempt indication.  When a response to the
	// ConnectionBind request is received, if it is a success, the TCP
	// connection on which it was sent is called the client data connection
	// corresponding to the peer.

	auto peerAttr = response.get<stun::XorPeerAddress>();
	if (!peerAttr || (peerAttr && peerAttr->family() != 1)) {
		assert(0);
		return;
	}

	auto connAttr = response.get<stun::ConnectionID>();
	if (!connAttr) {
		assert(0);
		return;
	}

	if (_observer.onPeerConnectionAttempt(*this, peerAttr->address())) //connAttr->value(),
		createAndBindConnection(connAttr->value(), peerAttr->address());
}

void TCPClient::handleConnectionBindResponse(const stun::Message& response)
{
	TraceL << "ConnectionBind success response" << endl;

	auto transaction = reinterpret_cast<stun::Transaction*>(response.opaque);
	auto req = reinterpret_cast<RelayConnectionBinding*>(transaction->socket->opaque);

	auto conn = connections().get(req->peerAddress, false);
	if (!conn) {
		assert(0);
		return;
	}

	// Data will now be transferred as-is to and from the peer.
	conn->Recv += sdelegate(this, &TCPClient::onRelayDataReceived);
	_observer.onRelayConnectionCreated(*this, conn, req->peerAddress);

	TraceL << "ConnectionBind success response: OK" << endl;
}

void TCPClient::handleConnectionBindErrorResponse(const stun::Message& response)
{
	TraceL << "ConnectionBind error response" << endl;

	auto transaction = reinterpret_cast<stun::Transaction*>(response.opaque);
	auto req = reinterpret_cast<RelayConnectionBinding*>(transaction->socket->opaque);

	// TODO: Handle properly

	freeConnection(req->peerAddress);
}

bool TCPClient::createAndBindConnection(UInt32 connectionID, const net::Address& peerAddress)
{
	//assert (!closed());
	//Mutex::ScopedLock lock(_mutex);

	TraceL << "Create and bind connection: " << peerAddress << endl;

	try {
		net::TCPSocket::Ptr conn(net::makeSocket<net::TCPSocket>()); //std::make_shared<net::TCPSocket>());
		conn->Connect += sdelegate(this, &TCPClient::onRelayConnectionConnect);
		conn->Error += sdelegate(this, &TCPClient::onRelayConnectionError);
		conn->Close += sdelegate(this, &TCPClient::onRelayConnectionClosed);

		auto req = new RelayConnectionBinding;
		req->connectionID = connectionID;
		req->peerAddress = peerAddress;
		conn->opaque = req;

		conn->connect(_options.serverAddr); // will throw on error

		_connections.add(peerAddress, conn);
		return true;
	}
	catch (std::exception& exc) {
		// Socket instance deleted via state callback
		ErrorL << "ConnectionBind Error: " << exc.what() << endl;
	}

	return false;
}

void TCPClient::onRelayConnectionConnect(void* sender)
{
	TraceL << "onRelayConnectionConnect" << endl;

	auto conn =reinterpret_cast<net::Socket*>(sender);
	conn->Connect -= sdelegate(this, &TCPClient::onRelayConnectionConnect);
	auto req = reinterpret_cast<RelayConnectionBinding*>(conn->opaque);
	assert(connections().has(req->peerAddress));

	// TODO: How to get peerAddress here?
	//net::TCPSocket& socket = _connections.get(peerAddress, conn);

	auto transaction = createTransaction(connections().get(req->peerAddress));
	transaction->request().setClass(stun::Message::Request);
	transaction->request().setMethod(stun::Message::ConnectionBind);

	auto connAttr = new stun::ConnectionID;
	connAttr->setValue(req->connectionID);
	transaction->request().add(connAttr);

	//assert(transaction->socket() == &conn);
	sendAuthenticatedTransaction(transaction);
}

void TCPClient::onRelayConnectionError(void* sender, const Error& /* error */)
{
	auto ptr = reinterpret_cast<net::Socket*>(sender);
	auto req = reinterpret_cast<RelayConnectionBinding*>(ptr->opaque);
	auto socket = _connections.get(req->peerAddress);

	TraceL << "Relay connection error: " << req->peerAddress << endl;
	assert(connections().has(req->peerAddress));

	_observer.onRelayConnectionError(*this, socket, req->peerAddress);
}

void TCPClient::onRelayConnectionClosed(void* sender)
{
	auto ptr = reinterpret_cast<net::Socket*>(sender);
	auto req = reinterpret_cast<RelayConnectionBinding*>(ptr->opaque);
	auto socket = _connections.get(req->peerAddress);

	TraceL << "Relay connection closed: " << req->peerAddress << endl;
	assert(connections().has(req->peerAddress));

	_observer.onRelayConnectionClosed(*this, socket, req->peerAddress);
	freeConnection(req->peerAddress);
}

void TCPClient::onRelayDataReceived(void* sender, const MutableBuffer& buffer, const net::Address& peerAddress)
{
	auto ptr = reinterpret_cast<net::Socket*>(sender);
	auto req = reinterpret_cast<RelayConnectionBinding*>(ptr->opaque);
	assert(connections().has(req->peerAddress));
	//TraceL << "Relay Data Received: " << peerAddress << ": " << req->peerAddress << endl;
	//assert(req->peerAddress == peerAddress);

	_observer.onRelayDataReceived(*this, bufferCast<const char*>(buffer), buffer.size(), req->peerAddress);
}

void TCPClient::freeConnection(const net::Address& peerAddress) //const net::TCPSocket::Ptr& socket)
{
	TraceL << "Freeing TCP connection: " << socket << endl;
	auto socket = connections().get(peerAddress);
	socket->Recv -= sdelegate(this, &TCPClient::onRelayDataReceived);
	socket->Connect -= sdelegate(this, &TCPClient::onRelayConnectionConnect);
	socket->Error -= sdelegate(this, &TCPClient::onRelayConnectionError);
	socket->Close -= sdelegate(this, &TCPClient::onRelayConnectionClosed);

	//assert(socket->base().refCount() == 1);
	//assert(connections().has(socket->address()));
	connections().remove(peerAddress); // destroy socket

	//auto map = connections().map();
	//for (auto conn : map) {
		//assert(it->second->base().refCount() == 1);
		// The connection will be removed via onRelayConnectionClosed
		//it->second->close();
	//}

	delete reinterpret_cast<RelayConnectionBinding*>(socket->opaque);
	//delete socket;
	//deleteLater<net::TCPSocket>(socket); // deferred
}

ConnectionManager& TCPClient::connections()
{
	//Mutex::ScopedLock lock(_mutex);
	return _connections;
}

int TCPClient::transportProtocol()
{
	return 6; // TCP
}

} } //  namespace scy::turn<commit_msg>Fix issue: no matching member function for call to 'get'<commit_after>//
// LibSourcey
// Copyright (C) 2005, Sourcey <http://sourcey.com>
//
// LibSourcey 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.
//
// LibSourcey 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

#include "scy/turn/client/tcpclient.h"
#include "scy/logger.h"
#include "scy/net/tcpsocket.h"
//#include "Poco/Format.h"

#include <assert.h>
#include <iostream>
#include <algorithm>

using namespace std;

namespace scy {
namespace turn {

TCPClient::TCPClient(TCPClientObserver& observer, const Client::Options& options) :
	Client(observer, options),
	_observer(observer)
{
	TraceL << "Create" << endl;

	_socket = net::makeSocket<net::TCPSocket>();
	//std::make_shared<net::TCPSocket>();
	//_socket.assign(new net::TCPSocket, false);
}

TCPClient::~TCPClient()
{
	TraceL << "Destroy" << endl;
	shutdown();
	//assert(connections().empty());
}

void TCPClient::initiate()
{
	Client::initiate();
}

void TCPClient::shutdown()
{
	TraceL << "Shutdown" << endl;

	//if (closed()) {
	//	TraceL << "Already closed" << endl;
	//	return;
	//}

	// Destroy transactions and stop timer
	Client::shutdown();

	{
		//Mutex::ScopedLock lock(_mutex);
		auto connections = _connections.map();
		TraceL << "Shutdown: Active connections: " << connections.size() << endl;
		for (auto it = connections.begin(); it != connections.end(); ++it) {
			//assert(it->second->base().refCount() == 1);

			// The connection will be removed via onRelayConnectionClosed
			it->second->close();
		}
		//assert(connections().empty());
	}

	TraceL << "Shutdown: OK" << endl;
}

void TCPClient::sendConnectRequest(const net::Address& peerAddress)
{
	// 4.3. Initiating a Connection
	//
	// To initiate a TCP connection to a peer, a client MUST send a Connect
	// request over the control connection for the desired allocation.  The
	// Connect request MUST include an XOR-PEER-ADDRESS attribute containing
	// the transport address of the peer to which a connection is desired.

	TraceL << "Send Connect request" << endl;

	auto transaction = createTransaction();
	//transaction->request().setType(stun::Message::Connect);
	//stun::Message request(stun::Message::Request, stun::Message::Allocate);
	transaction->request().setClass(stun::Message::Request);
	transaction->request().setMethod(stun::Message::Connect);

	auto peerAttr = new stun::XorPeerAddress;
	peerAttr->setAddress(peerAddress);
	//peerAttr->setFamily(1);
	//peerAttr->setPort(peerAddress.port());
	//peerAttr->setIP(peerAddress.host());
	transaction->request().add(peerAttr);

	sendAuthenticatedTransaction(transaction);
}

void TCPClient::sendData(const char* data, std::size_t size, const net::Address& peerAddress)
{
	TraceL << "Send data to " << peerAddress << endl;

	// Ensure permissions exist for the peer.
	if (!hasPermission(peerAddress.host()))
		throw std::runtime_error("No permission exists for peer: " + peerAddress.host());

	auto conn = connections().get(peerAddress, nullptr);
	if (!conn)
		throw std::runtime_error("No peer exists for: " + peerAddress.toString());

	conn->send(data, size);
}

bool TCPClient::handleResponse(const stun::Message& response)
{
	if (!Client::handleResponse(response)) {
		if (response.methodType() ==  stun::Message::Connect &&
			response.classType() == stun::Message::SuccessResponse)
			handleConnectResponse(response);

		else if (response.methodType() ==  stun::Message::ConnectionAttempt)
			handleConnectionAttemptIndication(response);

		else if (response.methodType() ==  stun::Message::ConnectionBind &&
			response.classType() == stun::Message::SuccessResponse)
			handleConnectionBindResponse(response);

		else if (response.methodType() ==  stun::Message::ConnectionBind &&
			response.classType() == stun::Message::ErrorResponse)
			handleConnectionBindErrorResponse(response);

		else if (response.methodType() ==  stun::Message::Connect &&
			response.classType() == stun::Message::ErrorResponse)
			handleConnectErrorResponse(response);

		else
			return false;
	}

	return true;
}

void TCPClient::handleConnectResponse(const stun::Message& response)
{
	// If the connection is successfully established, the client will
	// receive a success response.  That response will contain a
	// CONNECTION-ID attribute.  The client MUST initiate a new TCP
	// connection to the server, utilizing the same destination transport
	// address to which the control connection was established.  This
	// connection MUST be made using a different local transport address.
	// Authentication of the client by the server MUST use the same method
	// and credentials as for the control connection.  Once established, the
	// client MUST send a ConnectionBind request over the new connection.
	// That request MUST include the CONNECTION-ID attribute, echoed from
	// the Connect Success response.  When a response to the ConnectionBind
	// request is received, if it is a success, the TCP connection on which
	// it was sent is called the client data connection corresponding to the
	// peer.
	//

	auto transaction = reinterpret_cast<stun::Transaction*>(response.opaque);
	auto peerAttr = transaction->request().get<stun::XorPeerAddress>();
	if (!peerAttr || (peerAttr && peerAttr->family() != 1)) {
		assert(0);
		return;
	}

	auto connAttr = response.get<stun::ConnectionID>();
	if (!connAttr) {
		assert(0);
		return;
	}

	createAndBindConnection(connAttr->value(), peerAttr->address());
}

void TCPClient::handleConnectErrorResponse(const stun::Message& response)
{
	// If the result of the Connect request was an Error Response, and the
	// response code was 447 (Connection Timeout or Failure), it means that
	// the TURN server was unable to connect to the peer.  The client MAY
	// retry with the same XOR-PEER-ADDRESS attribute, but MUST wait at
	// least 10 seconds.
	//
	// As with any other request, multiple Connect requests MAY be sent
	// simultaneously.  However, Connect requests with the same XOR-PEER-
	// ADDRESS parameter MUST NOT be sent simultaneously.

	auto transaction = reinterpret_cast<stun::Transaction*>(response.opaque);
	auto peerAttr = transaction->request().get<stun::XorPeerAddress>();
	if (!peerAttr || (peerAttr && peerAttr->family() != 1)) {
		assert(0);
		return;
	}

	_observer.onRelayConnectionBindingFailed(*this, peerAttr->address());
}

void TCPClient::handleConnectionAttemptIndication(const stun::Message& response)
{
	// 4.4. Receiving a Connection
	//
	// After an Allocate request is successfully processed by the server,
	// the client will start receiving a ConnectionAttempt indication each
	// time a peer for which a permission has been installed attempts a new
	// connection to the relayed transport address.  This indication will
	// contain CONNECTION-ID and XOR-PEER-ADDRESS attributes.  If the client
	// wishes to accept this connection, it MUST initiate a new TCP
	// connection to the server, utilizing the same destination transport
	// address to which the control connection was established.  This
	// connection MUST be made using a different local transport address.
	// Authentication of the client by the server MUST use the same method
	// and credentials as for the control connection.  Once established, the
	// client MUST send a ConnectionBind request over the new connection.
	// That request MUST include the CONNECTION-ID attribute, echoed from
	// the ConnectionAttempt indication.  When a response to the
	// ConnectionBind request is received, if it is a success, the TCP
	// connection on which it was sent is called the client data connection
	// corresponding to the peer.

	auto peerAttr = response.get<stun::XorPeerAddress>();
	if (!peerAttr || (peerAttr && peerAttr->family() != 1)) {
		assert(0);
		return;
	}

	auto connAttr = response.get<stun::ConnectionID>();
	if (!connAttr) {
		assert(0);
		return;
	}

	if (_observer.onPeerConnectionAttempt(*this, peerAttr->address())) //connAttr->value(),
		createAndBindConnection(connAttr->value(), peerAttr->address());
}

void TCPClient::handleConnectionBindResponse(const stun::Message& response)
{
	TraceL << "ConnectionBind success response" << endl;

	auto transaction = reinterpret_cast<stun::Transaction*>(response.opaque);
	auto req = reinterpret_cast<RelayConnectionBinding*>(transaction->socket->opaque);

	auto conn = connections().get(req->peerAddress, nullptr);
	if (!conn) {
		assert(0);
		return;
	}

	// Data will now be transferred as-is to and from the peer.
	conn->Recv += sdelegate(this, &TCPClient::onRelayDataReceived);
	_observer.onRelayConnectionCreated(*this, conn, req->peerAddress);

	TraceL << "ConnectionBind success response: OK" << endl;
}

void TCPClient::handleConnectionBindErrorResponse(const stun::Message& response)
{
	TraceL << "ConnectionBind error response" << endl;

	auto transaction = reinterpret_cast<stun::Transaction*>(response.opaque);
	auto req = reinterpret_cast<RelayConnectionBinding*>(transaction->socket->opaque);

	// TODO: Handle properly

	freeConnection(req->peerAddress);
}

bool TCPClient::createAndBindConnection(UInt32 connectionID, const net::Address& peerAddress)
{
	//assert (!closed());
	//Mutex::ScopedLock lock(_mutex);

	TraceL << "Create and bind connection: " << peerAddress << endl;

	try {
		net::TCPSocket::Ptr conn(net::makeSocket<net::TCPSocket>()); //std::make_shared<net::TCPSocket>());
		conn->Connect += sdelegate(this, &TCPClient::onRelayConnectionConnect);
		conn->Error += sdelegate(this, &TCPClient::onRelayConnectionError);
		conn->Close += sdelegate(this, &TCPClient::onRelayConnectionClosed);

		auto req = new RelayConnectionBinding;
		req->connectionID = connectionID;
		req->peerAddress = peerAddress;
		conn->opaque = req;

		conn->connect(_options.serverAddr); // will throw on error

		_connections.add(peerAddress, conn);
		return true;
	}
	catch (std::exception& exc) {
		// Socket instance deleted via state callback
		ErrorL << "ConnectionBind Error: " << exc.what() << endl;
	}

	return false;
}

void TCPClient::onRelayConnectionConnect(void* sender)
{
	TraceL << "onRelayConnectionConnect" << endl;

	auto conn =reinterpret_cast<net::Socket*>(sender);
	conn->Connect -= sdelegate(this, &TCPClient::onRelayConnectionConnect);
	auto req = reinterpret_cast<RelayConnectionBinding*>(conn->opaque);
	assert(connections().has(req->peerAddress));

	// TODO: How to get peerAddress here?
	//net::TCPSocket& socket = _connections.get(peerAddress, conn);

	auto transaction = createTransaction(connections().get(req->peerAddress));
	transaction->request().setClass(stun::Message::Request);
	transaction->request().setMethod(stun::Message::ConnectionBind);

	auto connAttr = new stun::ConnectionID;
	connAttr->setValue(req->connectionID);
	transaction->request().add(connAttr);

	//assert(transaction->socket() == &conn);
	sendAuthenticatedTransaction(transaction);
}

void TCPClient::onRelayConnectionError(void* sender, const Error& /* error */)
{
	auto ptr = reinterpret_cast<net::Socket*>(sender);
	auto req = reinterpret_cast<RelayConnectionBinding*>(ptr->opaque);
	auto socket = _connections.get(req->peerAddress);

	TraceL << "Relay connection error: " << req->peerAddress << endl;
	assert(connections().has(req->peerAddress));

	_observer.onRelayConnectionError(*this, socket, req->peerAddress);
}

void TCPClient::onRelayConnectionClosed(void* sender)
{
	auto ptr = reinterpret_cast<net::Socket*>(sender);
	auto req = reinterpret_cast<RelayConnectionBinding*>(ptr->opaque);
	auto socket = _connections.get(req->peerAddress);

	TraceL << "Relay connection closed: " << req->peerAddress << endl;
	assert(connections().has(req->peerAddress));

	_observer.onRelayConnectionClosed(*this, socket, req->peerAddress);
	freeConnection(req->peerAddress);
}

void TCPClient::onRelayDataReceived(void* sender, const MutableBuffer& buffer, const net::Address& peerAddress)
{
	auto ptr = reinterpret_cast<net::Socket*>(sender);
	auto req = reinterpret_cast<RelayConnectionBinding*>(ptr->opaque);
	assert(connections().has(req->peerAddress));
	//TraceL << "Relay Data Received: " << peerAddress << ": " << req->peerAddress << endl;
	//assert(req->peerAddress == peerAddress);

	_observer.onRelayDataReceived(*this, bufferCast<const char*>(buffer), buffer.size(), req->peerAddress);
}

void TCPClient::freeConnection(const net::Address& peerAddress) //const net::TCPSocket::Ptr& socket)
{
	TraceL << "Freeing TCP connection: " << socket << endl;
	auto socket = connections().get(peerAddress);
	socket->Recv -= sdelegate(this, &TCPClient::onRelayDataReceived);
	socket->Connect -= sdelegate(this, &TCPClient::onRelayConnectionConnect);
	socket->Error -= sdelegate(this, &TCPClient::onRelayConnectionError);
	socket->Close -= sdelegate(this, &TCPClient::onRelayConnectionClosed);

	//assert(socket->base().refCount() == 1);
	//assert(connections().has(socket->address()));
	connections().remove(peerAddress); // destroy socket

	//auto map = connections().map();
	//for (auto conn : map) {
		//assert(it->second->base().refCount() == 1);
		// The connection will be removed via onRelayConnectionClosed
		//it->second->close();
	//}

	delete reinterpret_cast<RelayConnectionBinding*>(socket->opaque);
	//delete socket;
	//deleteLater<net::TCPSocket>(socket); // deferred
}

ConnectionManager& TCPClient::connections()
{
	//Mutex::ScopedLock lock(_mutex);
	return _connections;
}

int TCPClient::transportProtocol()
{
	return 6; // TCP
}

} } //  namespace scy::turn<|endoftext|>


<commit_before>#include <QInputDialog>

#include "QGCTCPLinkConfiguration.h"
#include "ui_QGCTCPLinkConfiguration.h"
#include <stdint.h>

QGCTCPLinkConfiguration::QGCTCPLinkConfiguration(TCPLink* link, QWidget *parent) :
    QWidget(parent),
    link(link),
    ui(new Ui::QGCTCPLinkConfiguration)
{
    ui->setupUi(this);
    uint16_t port = link->getPort();
    ui->portSpinBox->setValue(port);
    QString addr = link->getHostAddress().toString();
    ui->hostAddressLineEdit->setText(addr);
    connect(ui->portSpinBox, SIGNAL(valueChanged(int)), link, SLOT(setPort(int)));
    connect(ui->hostAddressLineEdit, SIGNAL(textChanged (const QString &)), link, SLOT(setAddress(const QString &)));
}

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

void QGCTCPLinkConfiguration::changeEvent(QEvent *e)
{
    QWidget::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}
<commit_msg>Comms: Fix basic issue with connecting using TCP<commit_after>#include <QInputDialog>

#include "QGCTCPLinkConfiguration.h"
#include "ui_QGCTCPLinkConfiguration.h"
#include <stdint.h>

QGCTCPLinkConfiguration::QGCTCPLinkConfiguration(TCPLink* link, QWidget *parent) :
    QWidget(parent),
    link(link),
    ui(new Ui::QGCTCPLinkConfiguration)
{
    ui->setupUi(this);
    uint16_t port = link->getPort();
    ui->portSpinBox->setValue(port);
    QString addr = link->getHostAddress().toString();
    ui->hostAddressLineEdit->setText(addr);
    connect(ui->portSpinBox, SIGNAL(valueChanged(int)), link, SLOT(setPort(int)));
    connect(ui->hostAddressLineEdit, SIGNAL(textChanged (const QString &)), link, SLOT(setHostAddress(const QString &)));
}

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

void QGCTCPLinkConfiguration::changeEvent(QEvent *e)
{
    QWidget::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}
<|endoftext|>


<commit_before>/*
 * ur_realtime_communication.cpp
 *
 * Copyright 2015 Thomas Timm Andersen
 *
 * 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.
 */

#include "ur_modern_driver/ur_realtime_communication.h"

UrRealtimeCommunication::UrRealtimeCommunication(
		std::condition_variable& msg_cond, std::string host,
		unsigned int safety_count_max) {
	robot_state_ = new RobotStateRT(msg_cond);
	bzero((char *) &serv_addr_, sizeof(serv_addr_));
	sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd_ < 0) {
		print_fatal("ERROR opening socket");
	}
	server_ = gethostbyname(host.c_str());
	if (server_ == NULL) {
		print_fatal("ERROR, no such host");
	}
	serv_addr_.sin_family = AF_INET;
	bcopy((char *) server_->h_addr, (char *)&serv_addr_.sin_addr.s_addr, server_->h_length);
	serv_addr_.sin_port = htons(30003);
	flag_ = 1;
	setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_, sizeof(int));
	setsockopt(sockfd_, IPPROTO_TCP, TCP_QUICKACK, (char *) &flag_, sizeof(int));
	setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, (char *) &flag_, sizeof(int));
	fcntl(sockfd_, F_SETFL, O_NONBLOCK);
	connected_ = false;
	keepalive_ = false;
	safety_count_ = safety_count_max + 1;
	safety_count_max_ = safety_count_max;
}

bool UrRealtimeCommunication::start() {
	fd_set writefds;
	struct timeval timeout;

	keepalive_ = true;
	print_debug("Realtime port: Connecting...");

	connect(sockfd_, (struct sockaddr *) &serv_addr_, sizeof(serv_addr_));
	FD_ZERO(&writefds);
	FD_SET(sockfd_, &writefds);
	timeout.tv_sec = 10;
	timeout.tv_usec = 0;
	select(sockfd_ + 1, NULL, &writefds, NULL, &timeout);
	unsigned int flag_len;
	getsockopt(sockfd_, SOL_SOCKET, SO_ERROR, &flag_, &flag_len);
	if (flag_ < 0) {
		print_fatal("Error connecting to RT port 30003");
		return false;
	}
	sockaddr_in name;
	socklen_t namelen = sizeof(name);
	int err = getsockname(sockfd_, (sockaddr*) &name, &namelen);
	if (err < 0) {
		print_fatal("Could not get local IP");
		close(sockfd_);
		return false;
	}
	char str[18];
	inet_ntop(AF_INET, &name.sin_addr, str, 18);
	local_ip_ = str;
	comThread_ = std::thread(&UrRealtimeCommunication::run, this);
	return true;
}

void UrRealtimeCommunication::halt() {
	keepalive_ = false;
	comThread_.join();
}

void UrRealtimeCommunication::addCommandToQueue(std::string inp) {
	int bytes_written;
	if (inp.back() != '\n') {
		inp.append("\n");
	}
	bytes_written = write(sockfd_, inp.c_str(), inp.length());
}

void UrRealtimeCommunication::setSpeed(double q0, double q1, double q2,
		double q3, double q4, double q5, double acc) {
	char cmd[1024];
	sprintf(cmd,
			"speedj([%1.5f, %1.5f, %1.5f, %1.5f, %1.5f, %1.5f], %f, 0.02)\n",
			q0, q1, q2, q3, q4, q5, acc);
	addCommandToQueue((std::string) (cmd));
	if (q0 != 0. or q1 != 0. or q2 != 0. or q3 != 0. or q4 != 0. or q5 != 0.) {
		//If a joint speed is set, make sure we stop it again after some time if the user doesn't
		safety_count_ = 0;
	}
}

void UrRealtimeCommunication::run() {
	uint8_t buf[2048];
	int bytes_read;
	bzero(buf, 2048);
	struct timeval timeout;
	fd_set readfds;
	FD_ZERO(&readfds);
	FD_SET(sockfd_, &readfds);
	print_debug("Realtime port: Got connection");
	connected_ = true;
	while (keepalive_) {
		while (connected_ && keepalive_) {
			timeout.tv_sec = 0; //do this each loop as selects modifies timeout
			timeout.tv_usec = 500000; // timeout of 0.5 sec
			select(sockfd_ + 1, &readfds, NULL, NULL, &timeout);
			bytes_read = read(sockfd_, buf, 2048);
			if (bytes_read > 0) {
				setsockopt(sockfd_, IPPROTO_TCP, TCP_QUICKACK, (char *) &flag_,
						sizeof(int));
				robot_state_->unpack(buf);
				if (safety_count_ == safety_count_max_) {
					setSpeed(0., 0., 0., 0., 0., 0.);
				}
				safety_count_ += 1;
			} else {
				connected_ = false;
				close(sockfd_);
			}
		}
		if (keepalive_) {
			//reconnect
			print_warning("Realtime port: No connection. Is controller crashed? Will try to reconnect in 10 seconds...");
			sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
			if (sockfd_ < 0) {
				print_fatal("ERROR opening socket");
			}
			flag_ = 1;
			setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_,
					sizeof(int));
			setsockopt(sockfd_, IPPROTO_TCP, TCP_QUICKACK, (char *) &flag_,
					sizeof(int));

			setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, (char *) &flag_,
					sizeof(int));
			fcntl(sockfd_, F_SETFL, O_NONBLOCK);
			while (keepalive_ && !connected_) {
				std::this_thread::sleep_for(std::chrono::seconds(10));
				fd_set writefds;
				keepalive_ = true;

				connect(sockfd_, (struct sockaddr *) &serv_addr_,
						sizeof(serv_addr_));
				FD_ZERO(&writefds);
				FD_SET(sockfd_, &writefds);
				select(sockfd_ + 1, NULL, &writefds, NULL, NULL);
				unsigned int flag_len;
				getsockopt(sockfd_, SOL_SOCKET, SO_ERROR, &flag_, &flag_len);
				if (flag_ < 0) {
					print_error("Error re-connecting to RT port 30003. Is controller started? Will try to reconnect in 10 seconds...");
				} else {
					connected_ = true;
					print_info("Realtime port: Reconnected");
				}
			}
		}
	}
	setSpeed(0., 0., 0., 0., 0., 0.);
	close(sockfd_);
}

void UrRealtimeCommunication::setSafetyCountMax(uint inp) {
	safety_count_max_ = inp;
}

std::string UrRealtimeCommunication::getLocalIp() {
	return local_ip_;
}
<commit_msg>Added a check to see that the robot is connected before attempting to write to the socket. Fixes #11<commit_after>/*
 * ur_realtime_communication.cpp
 *
 * Copyright 2015 Thomas Timm Andersen
 *
 * 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.
 */

#include "ur_modern_driver/ur_realtime_communication.h"

UrRealtimeCommunication::UrRealtimeCommunication(
		std::condition_variable& msg_cond, std::string host,
		unsigned int safety_count_max) {
	robot_state_ = new RobotStateRT(msg_cond);
	bzero((char *) &serv_addr_, sizeof(serv_addr_));
	sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
	if (sockfd_ < 0) {
		print_fatal("ERROR opening socket");
	}
	server_ = gethostbyname(host.c_str());
	if (server_ == NULL) {
		print_fatal("ERROR, no such host");
	}
	serv_addr_.sin_family = AF_INET;
	bcopy((char *) server_->h_addr, (char *)&serv_addr_.sin_addr.s_addr, server_->h_length);
	serv_addr_.sin_port = htons(30003);
	flag_ = 1;
	setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_, sizeof(int));
	setsockopt(sockfd_, IPPROTO_TCP, TCP_QUICKACK, (char *) &flag_, sizeof(int));
	setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, (char *) &flag_, sizeof(int));
	fcntl(sockfd_, F_SETFL, O_NONBLOCK);
	connected_ = false;
	keepalive_ = false;
	safety_count_ = safety_count_max + 1;
	safety_count_max_ = safety_count_max;
}

bool UrRealtimeCommunication::start() {
	fd_set writefds;
	struct timeval timeout;

	keepalive_ = true;
	print_debug("Realtime port: Connecting...");

	connect(sockfd_, (struct sockaddr *) &serv_addr_, sizeof(serv_addr_));
	FD_ZERO(&writefds);
	FD_SET(sockfd_, &writefds);
	timeout.tv_sec = 10;
	timeout.tv_usec = 0;
	select(sockfd_ + 1, NULL, &writefds, NULL, &timeout);
	unsigned int flag_len;
	getsockopt(sockfd_, SOL_SOCKET, SO_ERROR, &flag_, &flag_len);
	if (flag_ < 0) {
		print_fatal("Error connecting to RT port 30003");
		return false;
	}
	sockaddr_in name;
	socklen_t namelen = sizeof(name);
	int err = getsockname(sockfd_, (sockaddr*) &name, &namelen);
	if (err < 0) {
		print_fatal("Could not get local IP");
		close(sockfd_);
		return false;
	}
	char str[18];
	inet_ntop(AF_INET, &name.sin_addr, str, 18);
	local_ip_ = str;
	comThread_ = std::thread(&UrRealtimeCommunication::run, this);
	return true;
}

void UrRealtimeCommunication::halt() {
	keepalive_ = false;
	comThread_.join();
}

void UrRealtimeCommunication::addCommandToQueue(std::string inp) {
	int bytes_written;
	if (inp.back() != '\n') {
		inp.append("\n");
	}
	if (connected_)
		bytes_written = write(sockfd_, inp.c_str(), inp.length());
	else
		print_error("Could not send command \"" +inp + "\". The robot is not connected! Command is discarded" );
}

void UrRealtimeCommunication::setSpeed(double q0, double q1, double q2,
		double q3, double q4, double q5, double acc) {
	char cmd[1024];
	sprintf(cmd,
			"speedj([%1.5f, %1.5f, %1.5f, %1.5f, %1.5f, %1.5f], %f, 0.02)\n",
			q0, q1, q2, q3, q4, q5, acc);
	addCommandToQueue((std::string) (cmd));
	if (q0 != 0. or q1 != 0. or q2 != 0. or q3 != 0. or q4 != 0. or q5 != 0.) {
		//If a joint speed is set, make sure we stop it again after some time if the user doesn't
		safety_count_ = 0;
	}
}

void UrRealtimeCommunication::run() {
	uint8_t buf[2048];
	int bytes_read;
	bzero(buf, 2048);
	struct timeval timeout;
	fd_set readfds;
	FD_ZERO(&readfds);
	FD_SET(sockfd_, &readfds);
	print_debug("Realtime port: Got connection");
	connected_ = true;
	while (keepalive_) {
		while (connected_ && keepalive_) {
			timeout.tv_sec = 0; //do this each loop as selects modifies timeout
			timeout.tv_usec = 500000; // timeout of 0.5 sec
			select(sockfd_ + 1, &readfds, NULL, NULL, &timeout);
			bytes_read = read(sockfd_, buf, 2048);
			if (bytes_read > 0) {
				setsockopt(sockfd_, IPPROTO_TCP, TCP_QUICKACK, (char *) &flag_,
						sizeof(int));
				robot_state_->unpack(buf);
				if (safety_count_ == safety_count_max_) {
					setSpeed(0., 0., 0., 0., 0., 0.);
				}
				safety_count_ += 1;
			} else {
				connected_ = false;
				close(sockfd_);
			}
		}
		if (keepalive_) {
			//reconnect
			print_warning("Realtime port: No connection. Is controller crashed? Will try to reconnect in 10 seconds...");
			sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
			if (sockfd_ < 0) {
				print_fatal("ERROR opening socket");
			}
			flag_ = 1;
			setsockopt(sockfd_, IPPROTO_TCP, TCP_NODELAY, (char *) &flag_,
					sizeof(int));
			setsockopt(sockfd_, IPPROTO_TCP, TCP_QUICKACK, (char *) &flag_,
					sizeof(int));

			setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, (char *) &flag_,
					sizeof(int));
			fcntl(sockfd_, F_SETFL, O_NONBLOCK);
			while (keepalive_ && !connected_) {
				std::this_thread::sleep_for(std::chrono::seconds(10));
				fd_set writefds;
				keepalive_ = true;

				connect(sockfd_, (struct sockaddr *) &serv_addr_,
						sizeof(serv_addr_));
				FD_ZERO(&writefds);
				FD_SET(sockfd_, &writefds);
				select(sockfd_ + 1, NULL, &writefds, NULL, NULL);
				unsigned int flag_len;
				getsockopt(sockfd_, SOL_SOCKET, SO_ERROR, &flag_, &flag_len);
				if (flag_ < 0) {
					print_error("Error re-connecting to RT port 30003. Is controller started? Will try to reconnect in 10 seconds...");
				} else {
					connected_ = true;
					print_info("Realtime port: Reconnected");
				}
			}
		}
	}
	setSpeed(0., 0., 0., 0., 0., 0.);
	close(sockfd_);
}

void UrRealtimeCommunication::setSafetyCountMax(uint inp) {
	safety_count_max_ = inp;
}

std::string UrRealtimeCommunication::getLocalIp() {
	return local_ip_;
}
<|endoftext|>


<commit_before>// Copyright 2017 The Abseil Authors.
//
// 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.

#include "absl/base/internal/raw_logging.h"

#include <stddef.h>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/internal/atomic_hook.h"
#include "absl/base/log_severity.h"

// We know how to perform low-level writes to stderr in POSIX and Windows.  For
// these platforms, we define the token ABSL_LOW_LEVEL_WRITE_SUPPORTED.
// Much of raw_logging.cc becomes a no-op when we can't output messages,
// although a FATAL ABSL_RAW_LOG message will still abort the process.

// ABSL_HAVE_POSIX_WRITE is defined when the platform provides posix write()
// (as from unistd.h)
//
// This preprocessor token is also defined in raw_io.cc.  If you need to copy
// this, consider moving both to config.h instead.
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
    defined(__Fuchsia__) || defined(__native_client__)
#include <unistd.h>

#define ABSL_HAVE_POSIX_WRITE 1
#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
#else
#undef ABSL_HAVE_POSIX_WRITE
#endif

// ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall
//   syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len);
// for low level operations that want to avoid libc.
#if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__)
#include <sys/syscall.h>
#define ABSL_HAVE_SYSCALL_WRITE 1
#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
#else
#undef ABSL_HAVE_SYSCALL_WRITE
#endif

#ifdef _WIN32
#include <io.h>

#define ABSL_HAVE_RAW_IO 1
#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
#else
#undef ABSL_HAVE_RAW_IO
#endif

// TODO(gfalcon): We want raw-logging to work on as many platforms as possible.
// Explicitly #error out when not ABSL_LOW_LEVEL_WRITE_SUPPORTED, except for a
// whitelisted set of platforms for which we expect not to be able to raw log.

ABSL_CONST_INIT static absl::base_internal::AtomicHook<
    absl::raw_logging_internal::LogPrefixHook> log_prefix_hook;
ABSL_CONST_INIT static absl::base_internal::AtomicHook<
    absl::raw_logging_internal::AbortHook> abort_hook;

#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
static const char kTruncated[] = " ... (message truncated)\n";

// sprintf the format to the buffer, adjusting *buf and *size to reflect the
// consumed bytes, and return whether the message fit without truncation.  If
// truncation occurred, if possible leave room in the buffer for the message
// kTruncated[].
inline static bool VADoRawLog(char** buf, int* size, const char* format,
                              va_list ap) ABSL_PRINTF_ATTRIBUTE(3, 0);
inline static bool VADoRawLog(char** buf, int* size,
                              const char* format, va_list ap) {
  int n = vsnprintf(*buf, *size, format, ap);
  bool result = true;
  if (n < 0 || n > *size) {
    result = false;
    if (static_cast<size_t>(*size) > sizeof(kTruncated)) {
      n = *size - sizeof(kTruncated);  // room for truncation message
    } else {
      n = 0;                           // no room for truncation message
    }
  }
  *size -= n;
  *buf += n;
  return result;
}
#endif  // ABSL_LOW_LEVEL_WRITE_SUPPORTED

static constexpr int kLogBufSize = 3000;

namespace {

// CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths
// that invoke malloc() and getenv() that might acquire some locks.

// Helper for RawLog below.
// *DoRawLog writes to *buf of *size and move them past the written portion.
// It returns true iff there was no overflow or error.
bool DoRawLog(char** buf, int* size, const char* format, ...)
    ABSL_PRINTF_ATTRIBUTE(3, 4);
bool DoRawLog(char** buf, int* size, const char* format, ...) {
  va_list ap;
  va_start(ap, format);
  int n = vsnprintf(*buf, *size, format, ap);
  va_end(ap);
  if (n < 0 || n > *size) return false;
  *size -= n;
  *buf += n;
  return true;
}

void RawLogVA(absl::LogSeverity severity, const char* file, int line,
              const char* format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0);
void RawLogVA(absl::LogSeverity severity, const char* file, int line,
              const char* format, va_list ap) {
  char buffer[kLogBufSize];
  char* buf = buffer;
  int size = sizeof(buffer);
#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
  bool enabled = true;
#else
  bool enabled = false;
#endif

#ifdef ABSL_MIN_LOG_LEVEL
  if (static_cast<int>(severity) < ABSL_MIN_LOG_LEVEL &&
      severity < absl::LogSeverity::kFatal) {
    enabled = false;
  }
#endif

  auto log_prefix_hook_ptr = log_prefix_hook.Load();
  if (log_prefix_hook_ptr) {
    enabled = log_prefix_hook_ptr(severity, file, line, &buf, &size);
  } else {
    if (enabled) {
      DoRawLog(&buf, &size, "[%s : %d] RAW: ", file, line);
    }
  }
  const char* const prefix_end = buf;

#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
  if (enabled) {
    bool no_chop = VADoRawLog(&buf, &size, format, ap);
    if (no_chop) {
      DoRawLog(&buf, &size, "\n");
    } else {
      DoRawLog(&buf, &size, "%s", kTruncated);
    }
    absl::raw_logging_internal::SafeWriteToStderr(buffer, strlen(buffer));
  }
#else
  static_cast<void>(format);
  static_cast<void>(ap);
#endif

  // Abort the process after logging a FATAL message, even if the output itself
  // was suppressed.
  if (severity == absl::LogSeverity::kFatal) {
    abort_hook(file, line, buffer, prefix_end, buffer + kLogBufSize);
    abort();
  }
}

}  // namespace

namespace absl {
namespace raw_logging_internal {
void SafeWriteToStderr(const char *s, size_t len) {
#if defined(ABSL_HAVE_SYSCALL_WRITE)
  syscall(SYS_write, STDERR_FILENO, s, len);
#elif defined(ABSL_HAVE_POSIX_WRITE)
  write(STDERR_FILENO, s, len);
#elif defined(ABSL_HAVE_RAW_IO)
  _write(/* stderr */ 2, s, len);
#else
  // stderr logging unsupported on this platform
  (void) s;
  (void) len;
#endif
}

void RawLog(absl::LogSeverity severity, const char* file, int line,
            const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5);
void RawLog(absl::LogSeverity severity, const char* file, int line,
            const char* format, ...) {
  va_list ap;
  va_start(ap, format);
  RawLogVA(severity, file, line, format, ap);
  va_end(ap);
}

// Non-formatting version of RawLog().
//
// TODO(gfalcon): When string_view no longer depends on base, change this
// interface to take its message as a string_view instead.
static void DefaultInternalLog(absl::LogSeverity severity, const char* file,
                               int line, const std::string& message) {
  RawLog(severity, file, line, "%s", message.c_str());
}

bool RawLoggingFullySupported() {
#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
  return true;
#else  // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
  return false;
#endif  // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
}

ABSL_CONST_INIT absl::base_internal::AtomicHook<InternalLogFunction>
    internal_log_function(DefaultInternalLog);

void RegisterInternalLogFunction(InternalLogFunction func) {
  internal_log_function.Store(func);
}

}  // namespace raw_logging_internal
}  // namespace absl
<commit_msg>Export of internal Abseil changes. -- 50154b443b21a5123c50bebcfb866581e2e5b907 by Xiaoyi Zhang <zhangxy@google.com>:<commit_after>// Copyright 2017 The Abseil Authors.
//
// 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.

#include "absl/base/internal/raw_logging.h"

#include <stddef.h>
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/internal/atomic_hook.h"
#include "absl/base/log_severity.h"

// We know how to perform low-level writes to stderr in POSIX and Windows.  For
// these platforms, we define the token ABSL_LOW_LEVEL_WRITE_SUPPORTED.
// Much of raw_logging.cc becomes a no-op when we can't output messages,
// although a FATAL ABSL_RAW_LOG message will still abort the process.

// ABSL_HAVE_POSIX_WRITE is defined when the platform provides posix write()
// (as from unistd.h)
//
// This preprocessor token is also defined in raw_io.cc.  If you need to copy
// this, consider moving both to config.h instead.
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
    defined(__Fuchsia__) || defined(__native_client__)
#include <unistd.h>

#define ABSL_HAVE_POSIX_WRITE 1
#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
#else
#undef ABSL_HAVE_POSIX_WRITE
#endif

// ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall
//   syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len);
// for low level operations that want to avoid libc.
#if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__)
#include <sys/syscall.h>
#define ABSL_HAVE_SYSCALL_WRITE 1
#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
#else
#undef ABSL_HAVE_SYSCALL_WRITE
#endif

#ifdef _WIN32
#include <io.h>

#define ABSL_HAVE_RAW_IO 1
#define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1
#else
#undef ABSL_HAVE_RAW_IO
#endif

// TODO(gfalcon): We want raw-logging to work on as many platforms as possible.
// Explicitly #error out when not ABSL_LOW_LEVEL_WRITE_SUPPORTED, except for a
// whitelisted set of platforms for which we expect not to be able to raw log.

ABSL_CONST_INIT static absl::base_internal::AtomicHook<
    absl::raw_logging_internal::LogPrefixHook> log_prefix_hook;
ABSL_CONST_INIT static absl::base_internal::AtomicHook<
    absl::raw_logging_internal::AbortHook> abort_hook;

#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
static const char kTruncated[] = " ... (message truncated)\n";

// sprintf the format to the buffer, adjusting *buf and *size to reflect the
// consumed bytes, and return whether the message fit without truncation.  If
// truncation occurred, if possible leave room in the buffer for the message
// kTruncated[].
inline static bool VADoRawLog(char** buf, int* size, const char* format,
                              va_list ap) ABSL_PRINTF_ATTRIBUTE(3, 0);
inline static bool VADoRawLog(char** buf, int* size,
                              const char* format, va_list ap) {
  int n = vsnprintf(*buf, *size, format, ap);
  bool result = true;
  if (n < 0 || n > *size) {
    result = false;
    if (static_cast<size_t>(*size) > sizeof(kTruncated)) {
      n = *size - sizeof(kTruncated);  // room for truncation message
    } else {
      n = 0;                           // no room for truncation message
    }
  }
  *size -= n;
  *buf += n;
  return result;
}
#endif  // ABSL_LOW_LEVEL_WRITE_SUPPORTED

static constexpr int kLogBufSize = 3000;

namespace {

// CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths
// that invoke malloc() and getenv() that might acquire some locks.

// Helper for RawLog below.
// *DoRawLog writes to *buf of *size and move them past the written portion.
// It returns true iff there was no overflow or error.
bool DoRawLog(char** buf, int* size, const char* format, ...)
    ABSL_PRINTF_ATTRIBUTE(3, 4);
bool DoRawLog(char** buf, int* size, const char* format, ...) {
  va_list ap;
  va_start(ap, format);
  int n = vsnprintf(*buf, *size, format, ap);
  va_end(ap);
  if (n < 0 || n > *size) return false;
  *size -= n;
  *buf += n;
  return true;
}

void RawLogVA(absl::LogSeverity severity, const char* file, int line,
              const char* format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0);
void RawLogVA(absl::LogSeverity severity, const char* file, int line,
              const char* format, va_list ap) {
  char buffer[kLogBufSize];
  char* buf = buffer;
  int size = sizeof(buffer);
#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
  bool enabled = true;
#else
  bool enabled = false;
#endif

#ifdef ABSL_MIN_LOG_LEVEL
  if (severity < static_cast<absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) &&
      severity < absl::LogSeverity::kFatal) {
    enabled = false;
  }
#endif

  auto log_prefix_hook_ptr = log_prefix_hook.Load();
  if (log_prefix_hook_ptr) {
    enabled = log_prefix_hook_ptr(severity, file, line, &buf, &size);
  } else {
    if (enabled) {
      DoRawLog(&buf, &size, "[%s : %d] RAW: ", file, line);
    }
  }
  const char* const prefix_end = buf;

#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
  if (enabled) {
    bool no_chop = VADoRawLog(&buf, &size, format, ap);
    if (no_chop) {
      DoRawLog(&buf, &size, "\n");
    } else {
      DoRawLog(&buf, &size, "%s", kTruncated);
    }
    absl::raw_logging_internal::SafeWriteToStderr(buffer, strlen(buffer));
  }
#else
  static_cast<void>(format);
  static_cast<void>(ap);
#endif

  // Abort the process after logging a FATAL message, even if the output itself
  // was suppressed.
  if (severity == absl::LogSeverity::kFatal) {
    abort_hook(file, line, buffer, prefix_end, buffer + kLogBufSize);
    abort();
  }
}

}  // namespace

namespace absl {
namespace raw_logging_internal {
void SafeWriteToStderr(const char *s, size_t len) {
#if defined(ABSL_HAVE_SYSCALL_WRITE)
  syscall(SYS_write, STDERR_FILENO, s, len);
#elif defined(ABSL_HAVE_POSIX_WRITE)
  write(STDERR_FILENO, s, len);
#elif defined(ABSL_HAVE_RAW_IO)
  _write(/* stderr */ 2, s, len);
#else
  // stderr logging unsupported on this platform
  (void) s;
  (void) len;
#endif
}

void RawLog(absl::LogSeverity severity, const char* file, int line,
            const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5);
void RawLog(absl::LogSeverity severity, const char* file, int line,
            const char* format, ...) {
  va_list ap;
  va_start(ap, format);
  RawLogVA(severity, file, line, format, ap);
  va_end(ap);
}

// Non-formatting version of RawLog().
//
// TODO(gfalcon): When string_view no longer depends on base, change this
// interface to take its message as a string_view instead.
static void DefaultInternalLog(absl::LogSeverity severity, const char* file,
                               int line, const std::string& message) {
  RawLog(severity, file, line, "%s", message.c_str());
}

bool RawLoggingFullySupported() {
#ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED
  return true;
#else  // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
  return false;
#endif  // !ABSL_LOW_LEVEL_WRITE_SUPPORTED
}

ABSL_CONST_INIT absl::base_internal::AtomicHook<InternalLogFunction>
    internal_log_function(DefaultInternalLog);

void RegisterInternalLogFunction(InternalLogFunction func) {
  internal_log_function.Store(func);
}

}  // namespace raw_logging_internal
}  // namespace absl
<|endoftext|>


<commit_before>#include "physics.h"
#include "GLDebugDrawer.h"
#include "../state/state.h"
#include <iostream>
#include <BulletCollision/CollisionDispatch/btInternalEdgeUtility.h>
#include <BulletCollision/CollisionShapes/btTriangleShape.h>
#include <btBulletDynamicsCommon.h>

using namespace Physics;

Simulation::Simulation()
{
	m_broadphase = new btDbvtBroadphase();
	m_collisionConfiguration = new btDefaultCollisionConfiguration();
	m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
	m_solver = new btSequentialImpulseConstraintSolver;
	m_world = new btDiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, m_collisionConfiguration);

	m_world->setGravity(btVector3(0,-5,0));

	mb.request(Events::EventType::Input);
}

Simulation::~Simulation()
{
	/*
	delete m_world;
	delete m_solver;
	delete m_broadphase;
	delete m_dispatcher;
	delete m_collisionConfiguration;
	*/
}

// Tuning
// Credit to:
// http://bullet.googlecode.com/svn-history/r2704/trunk/Demos/ForkLiftDemo/ForkLiftDemo.cpp
// Also:
// https://code.google.com/p/bullet/source/browse/trunk/Demos/VehicleDemo/VehicleDemo.cpp

float	gVehicleSteering = 0.0f;
float	gEngineForce = 0.0f;
float	gBrakingForce = 0.0f;

float	wheelFriction = 5;
float	suspensionStiffness = 10;
float	suspensionDamping = 0.5f;
float	suspensionCompression = 0.3f;
float	rollInfluence = 0.025f; // Keep low to prevent car flipping

btScalar suspensionRestLength(0.1f);// Suspension Interval = rest +/- travel * 0.01
float	suspensionTravelcm = 20;

btRigidBody *Simulation::addRigidBody(double mass, const btTransform& startTransform, btCollisionShape* shape)
{
	btVector3 localInertia(0, 0, 0);
	if (mass != 0.0) {
		shape->calculateLocalInertia((btScalar)mass, localInertia);
	}

	btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);

	btRigidBody::btRigidBodyConstructionInfo cInfo((btScalar)mass,myMotionState,shape,localInertia);

	btRigidBody* body = new btRigidBody(cInfo);
	body->setContactProcessingThreshold(0.1f);

	m_world->addRigidBody(body);

	return body;
}

static bool CustomMaterialCombinerCallback(btManifoldPoint& cp,	const btCollisionObjectWrapper* colObj0Wrap,int partId0,int index0,const btCollisionObjectWrapper* colObj1Wrap,int partId1,int index1)
{
	btAdjustInternalEdgeContacts(cp,colObj1Wrap,colObj0Wrap, partId1,index1);
	return true;
}

extern ContactAddedCallback gContactAddedCallback;

int Simulation::loadWorld()
{
	// Create car
#define CAR_WIDTH (0.11f)
#define CAR_LENGTH (0.15f)
#define CAR_MASS (800.0f)

	btCollisionShape* chassisShape = new btBoxShape(btVector3(CAR_WIDTH, CAR_WIDTH, CAR_LENGTH));
	btCompoundShape* compound = new btCompoundShape();
	m_collisionShapes.push_back(chassisShape);
	m_collisionShapes.push_back(compound);

	btTransform localTrans;
	localTrans.setIdentity();
	localTrans.setOrigin(btVector3(0,0.05f, 0));
	compound->addChildShape(localTrans, chassisShape);

	btTransform tr;
	tr.setIdentity();
	tr.setOrigin(btVector3(0,2,0));		// This sets where the car initially spawns
	m_carChassis = addRigidBody(CAR_MASS, tr, compound);

	m_vehicleRayCaster = new btDefaultVehicleRaycaster(m_world);

	m_vehicle = new btRaycastVehicle(m_tuning,m_carChassis, m_vehicleRayCaster);
	m_carChassis->setActivationState(DISABLE_DEACTIVATION);
	m_vehicle->setCoordinateSystem(0,1,0);
	m_world->addVehicle(m_vehicle);

	float connectionHeight = 0.10f;
	btVector3 wheelDirectionCS0(0,-1,0);
	btVector3 wheelAxleCS(-1,0,0);

#define CON1 (CAR_WIDTH)
#define CON2 (CAR_LENGTH)

	float	wheelRadius = 0.15f;

	// Setup front 2 wheels
	bool isFrontWheel=true;
	btVector3 connectionPointCS0(CON1,connectionHeight,CON2);
	m_vehicle->addWheel(connectionPointCS0,wheelDirectionCS0,wheelAxleCS,suspensionRestLength,wheelRadius,m_tuning,isFrontWheel);

	connectionPointCS0 = btVector3(-CON1,connectionHeight,CON2);
	m_vehicle->addWheel(connectionPointCS0,wheelDirectionCS0,wheelAxleCS,suspensionRestLength,wheelRadius,m_tuning,isFrontWheel);

	// Setup rear  2 wheels
	isFrontWheel = false;

	connectionPointCS0 = btVector3(-CON1,connectionHeight,-CON2);
	m_vehicle->addWheel(connectionPointCS0,wheelDirectionCS0,wheelAxleCS,suspensionRestLength,wheelRadius,m_tuning,isFrontWheel);

	connectionPointCS0 = btVector3(CON1,connectionHeight,-CON2);
	m_vehicle->addWheel(connectionPointCS0,wheelDirectionCS0,wheelAxleCS,suspensionRestLength,wheelRadius,m_tuning,isFrontWheel);

	for (int i=0;i<m_vehicle->getNumWheels();i++)
	{
		btWheelInfo& wheel = m_vehicle->getWheelInfo(i);

		wheel.m_maxSuspensionTravelCm = suspensionTravelcm;
		wheel.m_suspensionStiffness = suspensionStiffness;
		wheel.m_wheelsDampingRelaxation = suspensionDamping;
		wheel.m_wheelsDampingCompression = suspensionCompression;
		wheel.m_frictionSlip = wheelFriction;
		wheel.m_rollInfluence = rollInfluence;

	}

	// Add map
	// Credit to http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6662
	// for solution to wheels bouncing off triangle edges
	gContactAddedCallback = CustomMaterialCombinerCallback;
	StateData *state = GetMutState();
	btBvhTriangleMeshShape *arenaShape = new btBvhTriangleMeshShape(state->bttmArena, true, true);

	m_arena = addRigidBody(0.0, btTransform(btQuaternion(0,0,0,1),btVector3(0,0,0)), arenaShape);
	m_arena->setCollisionFlags(m_arena->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
	btTriangleInfoMap* triangleInfoMap = new btTriangleInfoMap();
	btGenerateInternalEdgeInfo(arenaShape, triangleInfoMap);

	for (int i=0;i<m_vehicle->getNumWheels();i++)
	{
		//synchronize the wheels with the (interpolated) chassis worldtransform
		m_vehicle->updateWheelTransform(i,true);
	}

	return 0;
}

void Simulation::step(double seconds)
{
#define STEER_MAX_ANGLE (25)
#define ENGINE_MAX_FORCE (1500)
#define BRAKE_MAX_FORCE (1000)
#define E_BRAKE_FORCE (200)
#define MAX_SPEED (25)

	for ( Events::Event *event : (mb.checkMail()) )
	{
		switch ( event->type )
		{
		case Events::EventType::Input:
		{
			Events::InputEvent *input = (Events::InputEvent *)event;

			Real fTurnSqr = input->leftThumbStickRL * input->leftThumbStickRL;

			gVehicleSteering = DEGTORAD(STEER_MAX_ANGLE) * ( input->leftThumbStickRL < 0 ? -fTurnSqr : fTurnSqr );

			gBrakingForce = input->bPressed ? E_BRAKE_FORCE : 0;
			gEngineForce = ENGINE_MAX_FORCE * input->rightTrigger - BRAKE_MAX_FORCE * input->leftTrigger;

			if( GetState().key_map['r'] )
			{
				btTransform trans;
				trans.setOrigin( btVector3( 0, 5, 0 ) );
				trans.setRotation( btQuaternion( 0, 0, 0, 1 ) );
				m_vehicle->getRigidBody()->setWorldTransform( trans );
				m_vehicle->getRigidBody()->setLinearVelocity(btVector3(0,0,0));
			}

			if( input->yPressed )
			{
				btTransform trans = m_vehicle->getRigidBody()->getWorldTransform();
				btVector3 orig = trans.getOrigin();
				orig.setY( orig.getY() + 0.01f );
				trans.setOrigin( orig );
				trans.setRotation( btQuaternion( 0, 0, 0, 1 ) );
				m_vehicle->getRigidBody()->setWorldTransform( trans );
			}

//			DEBUGOUT("Bforce: %lf, Eforce: %lf, Steer: %f\n", gBrakingForce, gEngineForce, gVehicleSteering);
//			DEBUGOUT("Speed: %f\n", (float)ABS( m_vehicle->getCurrentSpeedKmHour() ) );
		}
		default:
			break;
		}
	}
	mb.emptyMail();

	if( ABS( m_vehicle->getCurrentSpeedKmHour() ) > MAX_SPEED )
		gEngineForce = 0;

	// Apply steering to front wheels
	m_vehicle->setSteeringValue(gVehicleSteering, 0);
	m_vehicle->setSteeringValue(gVehicleSteering, 1);

	// Apply braking and engine force to rear wheels
	m_vehicle->applyEngineForce(gEngineForce, 0);
	m_vehicle->applyEngineForce(gEngineForce, 1);
	m_vehicle->setBrake(gBrakingForce, 2);
	m_vehicle->setBrake(gBrakingForce, 3);

	m_world->stepSimulation((btScalar)seconds, 10);

	if( m_vehicle->getRigidBody()->getWorldTransform().getOrigin().y() < -1.0f )
	{
		btTransform trans;
		trans.setOrigin( btVector3( 0, 1, 0 ) );
		trans.setRotation( btQuaternion( 0, 0, 0, 1 ) );
		m_vehicle->getRigidBody()->setWorldTransform( trans );
		m_vehicle->getRigidBody()->setLinearVelocity(btVector3(0,5,0));
	}

	UpdateGameState();
}

// Updates the car placement in the world state
void Simulation::UpdateGameState()
{
	// -- Kart position ------------------------
	StateData *state = GetMutState();
	btTransform car1 = m_vehicle->getChassisWorldTransform();

	btVector3 pos = car1.getOrigin();
	state->Karts[0].vPos.x = (Real)pos.getX();
	state->Karts[0].vPos.y = (Real)pos.getY();
	state->Karts[0].vPos.z = (Real)pos.getZ();

	btQuaternion rot = car1.getRotation();
	state->Karts[0].qOrient.x = (Real)rot.getX();
	state->Karts[0].qOrient.y = (Real)rot.getY();
	state->Karts[0].qOrient.z = (Real)rot.getZ();
	state->Karts[0].qOrient.w = (Real)-rot.getW();

	// -- Chase Cam ----------------------------
	// Focus on car
	state->Camera.vFocus = state->Karts[0].vPos;

	// Get car direction
	btVector3 camera =  m_vehicle->getForwardVector() / m_vehicle->getForwardVector().length();
	camera = camera.rotate(btVector3(0,1,0), DEGTORAD(90)); // forward vector points left, somehow

	// Mixin car direction history
	Real DIR_DROPOFF = 0.05;
	static btVector3 dir_history = camera;
	dir_history *= (1.0 - DIR_DROPOFF);
	dir_history += camera * DIR_DROPOFF;
	camera = dir_history;

	// Mixin historic speeds
	Real SPEED_DROPOFF = 0.001;
	Real speed = m_vehicle->getCurrentSpeedKmHour();
	static Real speed_history = speed;
	speed_history *= (1.0 - SPEED_DROPOFF);
	speed_history += speed * SPEED_DROPOFF;

	// Make camera focus during acceleration
	Real diff = (speed - speed_history) / MAX_SPEED;
	diff = diff > 0.0 ? diff : 0;
	diff *= diff;
	diff = (pow(2, diff) - 1) / 1;
	diff = MIN(diff, 0.3);
	Real DIFF_DROPOFF = 0.1;
	static Real diff_history = diff;
	if (diff > 0.0) {
		diff_history *= (1.0 - DIFF_DROPOFF);
		diff_history += diff * DIFF_DROPOFF;
	}
	diff = diff_history;
	DEBUGOUT("diff = %lf\n", diff);

	camera.setY((0.6 * (1 - diff * 2) + camera.getY()));
	camera *= 2.0 - diff * 2;
	state->Camera.fFOV = 60.0f * (1 - diff);

	// Add camera vector to kart position for camera position
	state->Camera.vPos = state->Karts[0].vPos;
	state->Camera.vPos.x += camera.getX();
	state->Camera.vPos.y += camera.getY();
	state->Camera.vPos.z += camera.getZ();
}

void Simulation::enableDebugView()
{
}
<commit_msg>Make camera focus chase car<commit_after>#include "physics.h"
#include "GLDebugDrawer.h"
#include "../state/state.h"
#include <iostream>
#include <BulletCollision/CollisionDispatch/btInternalEdgeUtility.h>
#include <BulletCollision/CollisionShapes/btTriangleShape.h>
#include <btBulletDynamicsCommon.h>

using namespace Physics;

Simulation::Simulation()
{
	m_broadphase = new btDbvtBroadphase();
	m_collisionConfiguration = new btDefaultCollisionConfiguration();
	m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
	m_solver = new btSequentialImpulseConstraintSolver;
	m_world = new btDiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, m_collisionConfiguration);

	m_world->setGravity(btVector3(0,-5,0));

	mb.request(Events::EventType::Input);
}

Simulation::~Simulation()
{
	/*
	delete m_world;
	delete m_solver;
	delete m_broadphase;
	delete m_dispatcher;
	delete m_collisionConfiguration;
	*/
}

// Tuning
// Credit to:
// http://bullet.googlecode.com/svn-history/r2704/trunk/Demos/ForkLiftDemo/ForkLiftDemo.cpp
// Also:
// https://code.google.com/p/bullet/source/browse/trunk/Demos/VehicleDemo/VehicleDemo.cpp

float	gVehicleSteering = 0.0f;
float	gEngineForce = 0.0f;
float	gBrakingForce = 0.0f;

float	wheelFriction = 5;
float	suspensionStiffness = 10;
float	suspensionDamping = 0.5f;
float	suspensionCompression = 0.3f;
float	rollInfluence = 0.015f; // Keep low to prevent car flipping

btScalar suspensionRestLength(0.1f);// Suspension Interval = rest +/- travel * 0.01
float	suspensionTravelcm = 20;

btRigidBody *Simulation::addRigidBody(double mass, const btTransform& startTransform, btCollisionShape* shape)
{
	btVector3 localInertia(0, 0, 0);
	if (mass != 0.0) {
		shape->calculateLocalInertia((btScalar)mass, localInertia);
	}

	btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);

	btRigidBody::btRigidBodyConstructionInfo cInfo((btScalar)mass,myMotionState,shape,localInertia);

	btRigidBody* body = new btRigidBody(cInfo);
	body->setContactProcessingThreshold(0.1f);

	m_world->addRigidBody(body);

	return body;
}

static bool CustomMaterialCombinerCallback(btManifoldPoint& cp,	const btCollisionObjectWrapper* colObj0Wrap,int partId0,int index0,const btCollisionObjectWrapper* colObj1Wrap,int partId1,int index1)
{
	btAdjustInternalEdgeContacts(cp,colObj1Wrap,colObj0Wrap, partId1,index1);
	return true;
}

extern ContactAddedCallback gContactAddedCallback;

int Simulation::loadWorld()
{
	// Create car
#define CAR_WIDTH (0.11f)
#define CAR_LENGTH (0.15f)
#define CAR_MASS (800.0f)

	btCollisionShape* chassisShape = new btBoxShape(btVector3(CAR_WIDTH, CAR_WIDTH, CAR_LENGTH));
	btCompoundShape* compound = new btCompoundShape();
	m_collisionShapes.push_back(chassisShape);
	m_collisionShapes.push_back(compound);

	btTransform localTrans;
	localTrans.setIdentity();
	localTrans.setOrigin(btVector3(0,0.05f, 0));
	compound->addChildShape(localTrans, chassisShape);

	btTransform tr;
	tr.setIdentity();
	tr.setOrigin(btVector3(0,2,0));		// This sets where the car initially spawns
	m_carChassis = addRigidBody(CAR_MASS, tr, compound);

	m_vehicleRayCaster = new btDefaultVehicleRaycaster(m_world);

	m_vehicle = new btRaycastVehicle(m_tuning,m_carChassis, m_vehicleRayCaster);
	m_carChassis->setActivationState(DISABLE_DEACTIVATION);
	m_vehicle->setCoordinateSystem(0,1,0);
	m_world->addVehicle(m_vehicle);

	float connectionHeight = 0.10f;
	btVector3 wheelDirectionCS0(0,-1,0);
	btVector3 wheelAxleCS(-1,0,0);

#define CON1 (CAR_WIDTH)
#define CON2 (CAR_LENGTH)

	float	wheelRadius = 0.15f;

	// Setup front 2 wheels
	bool isFrontWheel=true;
	btVector3 connectionPointCS0(CON1,connectionHeight,CON2);
	m_vehicle->addWheel(connectionPointCS0,wheelDirectionCS0,wheelAxleCS,suspensionRestLength,wheelRadius,m_tuning,isFrontWheel);

	connectionPointCS0 = btVector3(-CON1,connectionHeight,CON2);
	m_vehicle->addWheel(connectionPointCS0,wheelDirectionCS0,wheelAxleCS,suspensionRestLength,wheelRadius,m_tuning,isFrontWheel);

	// Setup rear  2 wheels
	isFrontWheel = false;

	connectionPointCS0 = btVector3(-CON1,connectionHeight,-CON2);
	m_vehicle->addWheel(connectionPointCS0,wheelDirectionCS0,wheelAxleCS,suspensionRestLength,wheelRadius,m_tuning,isFrontWheel);

	connectionPointCS0 = btVector3(CON1,connectionHeight,-CON2);
	m_vehicle->addWheel(connectionPointCS0,wheelDirectionCS0,wheelAxleCS,suspensionRestLength,wheelRadius,m_tuning,isFrontWheel);

	for (int i=0;i<m_vehicle->getNumWheels();i++)
	{
		btWheelInfo& wheel = m_vehicle->getWheelInfo(i);

		wheel.m_maxSuspensionTravelCm = suspensionTravelcm;
		wheel.m_suspensionStiffness = suspensionStiffness;
		wheel.m_wheelsDampingRelaxation = suspensionDamping;
		wheel.m_wheelsDampingCompression = suspensionCompression;
		wheel.m_frictionSlip = wheelFriction;
		wheel.m_rollInfluence = rollInfluence;

	}

	// Add map
	// Credit to http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6662
	// for solution to wheels bouncing off triangle edges
	gContactAddedCallback = CustomMaterialCombinerCallback;
	StateData *state = GetMutState();
	btBvhTriangleMeshShape *arenaShape = new btBvhTriangleMeshShape(state->bttmArena, true, true);

	m_arena = addRigidBody(0.0, btTransform(btQuaternion(0,0,0,1),btVector3(0,0,0)), arenaShape);
	m_arena->setCollisionFlags(m_arena->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
	btTriangleInfoMap* triangleInfoMap = new btTriangleInfoMap();
	btGenerateInternalEdgeInfo(arenaShape, triangleInfoMap);

	for (int i=0;i<m_vehicle->getNumWheels();i++)
	{
		//synchronize the wheels with the (interpolated) chassis worldtransform
		m_vehicle->updateWheelTransform(i,true);
	}

	return 0;
}

void Simulation::step(double seconds)
{
#define STEER_MAX_ANGLE (25)
#define ENGINE_MAX_FORCE (1500)
#define BRAKE_MAX_FORCE (1000)
#define E_BRAKE_FORCE (200)
#define MAX_SPEED (25.0)

	for ( Events::Event *event : (mb.checkMail()) )
	{
		switch ( event->type )
		{
		case Events::EventType::Input:
		{
			Events::InputEvent *input = (Events::InputEvent *)event;

			Real fTurnSqr = input->leftThumbStickRL * input->leftThumbStickRL;

			gVehicleSteering = DEGTORAD(STEER_MAX_ANGLE) * ( input->leftThumbStickRL < 0 ? -fTurnSqr : fTurnSqr );

			gBrakingForce = input->bPressed ? E_BRAKE_FORCE : 0;
			gEngineForce = ENGINE_MAX_FORCE * input->rightTrigger - BRAKE_MAX_FORCE * input->leftTrigger;

			if( GetState().key_map['r'] )
			{
				btTransform trans;
				trans.setOrigin( btVector3( 0, 5, 0 ) );
				trans.setRotation( btQuaternion( 0, 0, 0, 1 ) );
				m_vehicle->getRigidBody()->setWorldTransform( trans );
				m_vehicle->getRigidBody()->setLinearVelocity(btVector3(0,0,0));
			}

			if( input->yPressed )
			{
				btTransform trans = m_vehicle->getRigidBody()->getWorldTransform();
				btVector3 orig = trans.getOrigin();
				orig.setY( orig.getY() + 0.01f );
				trans.setOrigin( orig );
				trans.setRotation( btQuaternion( 0, 0, 0, 1 ) );
				m_vehicle->getRigidBody()->setWorldTransform( trans );
			}

//			DEBUGOUT("Bforce: %lf, Eforce: %lf, Steer: %f\n", gBrakingForce, gEngineForce, gVehicleSteering);
//			DEBUGOUT("Speed: %f\n", (float)ABS( m_vehicle->getCurrentSpeedKmHour() ) );
		}
		default:
			break;
		}
	}
	mb.emptyMail();

	if( ABS( m_vehicle->getCurrentSpeedKmHour() ) > MAX_SPEED )
		gEngineForce = 0;

	// Apply steering to front wheels
	m_vehicle->setSteeringValue(gVehicleSteering, 0);
	m_vehicle->setSteeringValue(gVehicleSteering, 1);

	// Apply braking and engine force to rear wheels
	m_vehicle->applyEngineForce(gEngineForce, 0);
	m_vehicle->applyEngineForce(gEngineForce, 1);
	m_vehicle->setBrake(gBrakingForce, 2);
	m_vehicle->setBrake(gBrakingForce, 3);

	m_world->stepSimulation((btScalar)seconds, 10);

	if( m_vehicle->getRigidBody()->getWorldTransform().getOrigin().y() < -1.0f )
	{
		btTransform trans;
		trans.setOrigin( btVector3( 0, 5, 0 ) );
		trans.setRotation( btQuaternion( 0, 0, 0, 1 ) );
		m_vehicle->getRigidBody()->setWorldTransform( trans );
		m_vehicle->getRigidBody()->setLinearVelocity(btVector3(0,0,0));
	}

	UpdateGameState();
}

// Updates the car placement in the world state
void Simulation::UpdateGameState()
{
	// -- Kart position ------------------------
	StateData *state = GetMutState();
	btTransform car1 = m_vehicle->getChassisWorldTransform();

	btVector3 pos = car1.getOrigin();
	state->Karts[0].vPos.x = (Real)pos.getX();
	state->Karts[0].vPos.y = (Real)pos.getY();
	state->Karts[0].vPos.z = (Real)pos.getZ();

	btQuaternion rot = car1.getRotation();
	state->Karts[0].qOrient.x = (Real)rot.getX();
	state->Karts[0].qOrient.y = (Real)rot.getY();
	state->Karts[0].qOrient.z = (Real)rot.getZ();
	state->Karts[0].qOrient.w = (Real)-rot.getW();

	// -- Chase Cam ----------------------------
	// Focus on car
	Vector3 focus = state->Karts[0].vPos;
	Real FOCUS_DROPOFF = 0.5;
	static Vector3 focus_history = focus;
	focus_history *= (1 - FOCUS_DROPOFF);
	focus_history = focus_history + focus * FOCUS_DROPOFF;
	state->Camera.vFocus = focus_history;

	// Get car direction
	btVector3 camera =  m_vehicle->getForwardVector() / m_vehicle->getForwardVector().length();
	camera = camera.rotate(btVector3(0,1,0), DEGTORAD(90)); // forward vector points left, somehow
	if (camera.getY() < 0.4) {
		camera.setY(0.4);
	}

	// Mixin car direction history
	Real DIR_DROPOFF = 0.05;
	static btVector3 dir_history = camera;
	dir_history *= (1.0 - DIR_DROPOFF);
	dir_history += camera * DIR_DROPOFF;
	camera = dir_history;

	// Mixin historic speeds
	Real SPEED_DROPOFF = 0.001;
	Real speed = m_vehicle->getCurrentSpeedKmHour();
	static Real speed_history = speed;
	speed_history *= (1.0 - SPEED_DROPOFF);
	speed_history += speed * SPEED_DROPOFF;

	// Make camera focus during acceleration
	Real diff = (speed - speed_history) / MAX_SPEED;
	diff = diff > 0.0 ? diff : 0;
	diff *= diff;
	diff = (pow(2, diff) - 1) / 1;
	diff = MIN(diff, 0.3);
	Real DIFF_DROPOFF = 0.1;
	static Real diff_history = diff;
	if (diff > 0.0) {
		diff_history *= (1.0 - DIFF_DROPOFF);
		diff_history += diff * DIFF_DROPOFF;
	}
	diff = diff_history;
	DEBUGOUT("diff = %lf\n", diff);

	camera.setY((0.9 * (1 - MAX((diff * 3.7), 0.7)) + camera.getY()));
	camera *= 3.0 - diff * 1;
	state->Camera.fFOV = 60.0f * (1 - diff * 1.4);

	// Add camera vector to kart position for camera position
	state->Camera.vPos = state->Karts[0].vPos;
	state->Camera.vPos.x += camera.getX();
	state->Camera.vPos.y += camera.getY();
	state->Camera.vPos.z += camera.getZ();
}

void Simulation::enableDebugView()
{
}
<|endoftext|>


<commit_before>/**
 * These codes are licensed under the Unlicense.
 * http://unlicense.org
 */

#ifndef FURFURYLIC_3486BCC7_6E1A_41C9_9FAA_F6A71268A80F
#define FURFURYLIC_3486BCC7_6E1A_41C9_9FAA_F6A71268A80F

#include <cstddef>
#include <type_traits>

namespace commata { namespace test {

template <class T, bool Pocca, bool Pocma, bool Pocs, bool Iae = false>
class identified_allocator
{
    std::size_t id_;

public:
    template <class U>
    struct rebind
    {
        using other = identified_allocator<U, Pocca, Pocma, Pocs, Iae>;
    };

    using value_type = T;

    identified_allocator() noexcept :
        identified_allocator(static_cast<std::size_t>(-1))
    {}

    explicit identified_allocator(std::size_t id) noexcept :
        id_(id)
    {}

    // To make a rebound copy
    template <class U>
    explicit identified_allocator(
            const identified_allocator<U, Pocca, Pocma, Pocs, Iae>& other)
        noexcept :
        id_(other.id())
    {}

    // Copy/move ops are defaulted

    auto allocate(std::size_t n)
    {
        return reinterpret_cast<T*>(new char[sizeof(T) * n]);   // throw
    }

    void deallocate(T* p, std::size_t) noexcept
    {
        delete [] reinterpret_cast<char*>(p);
    }

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4100)
#endif
    template <class U>
    bool operator==(const identified_allocator<
        U, Pocca, Pocma, Pocs, Iae>& other) const noexcept
    {
        return Iae || (id() == other.id());
    }
#ifdef _MSC_VER
#pragma warning(pop)
#endif

    template <class U>
    bool operator!=(const identified_allocator<
        U, Pocca, Pocma, Pocs, Iae>& other) const noexcept
    {
        return !(*this == other);
    }

    using propagate_on_container_copy_assignment =
        std::integral_constant<bool, Pocca>;
    using propagate_on_container_move_assignment =
        std::integral_constant<bool, Pocma>;
    using propagate_on_container_swap =
        std::integral_constant<bool, Pocs>;

    std::size_t id() const noexcept
    {
        return id_;
    }
};

}}

#endif
<commit_msg>Employ tag dispatching for identified_allocator<commit_after>/**
 * These codes are licensed under the Unlicense.
 * http://unlicense.org
 */

#ifndef FURFURYLIC_3486BCC7_6E1A_41C9_9FAA_F6A71268A80F
#define FURFURYLIC_3486BCC7_6E1A_41C9_9FAA_F6A71268A80F

#include <cstddef>
#include <type_traits>

namespace commata { namespace test {

template <class T, bool Pocca, bool Pocma, bool Pocs, bool Iae = false>
class identified_allocator
{
    std::size_t id_;

public:
    template <class U>
    struct rebind
    {
        using other = identified_allocator<U, Pocca, Pocma, Pocs, Iae>;
    };

    using value_type = T;

    identified_allocator() noexcept :
        identified_allocator(static_cast<std::size_t>(-1))
    {}

    explicit identified_allocator(std::size_t id) noexcept :
        id_(id)
    {}

    // To make a rebound copy
    template <class U>
    explicit identified_allocator(
            const identified_allocator<U, Pocca, Pocma, Pocs, Iae>& other)
        noexcept :
        id_(other.id())
    {}

    // Copy/move ops are defaulted

    auto allocate(std::size_t n)
    {
        return reinterpret_cast<T*>(new char[sizeof(T) * n]);   // throw
    }

    void deallocate(T* p, std::size_t) noexcept
    {
        delete [] reinterpret_cast<char*>(p);
    }

    template <class U>
    bool operator==(const identified_allocator<
        U, Pocca, Pocma, Pocs, Iae>& other) const noexcept
    {
        return equal_to(std::integral_constant<bool, Iae>(), other);
    }

    template <class U>
    bool operator!=(const identified_allocator<
        U, Pocca, Pocma, Pocs, Iae>& other) const noexcept
    {
        return !(*this == other);
    }

    using propagate_on_container_copy_assignment =
        std::integral_constant<bool, Pocca>;
    using propagate_on_container_move_assignment =
        std::integral_constant<bool, Pocma>;
    using propagate_on_container_swap =
        std::integral_constant<bool, Pocs>;

    std::size_t id() const noexcept
    {
        return id_;
    }

private:
    template <class U>
    constexpr bool equal_to(std::true_type, const identified_allocator<
        U, Pocca, Pocma, Pocs, Iae>&) const noexcept
    {
        return true;
    }

    template <class U>
    bool equal_to(std::false_type, const identified_allocator<
        U, Pocca, Pocma, Pocs, Iae>& other) const noexcept
    {
        return id() == other.id();
    }
};

}}

#endif
<|endoftext|>


<commit_before>/*
    Copyright (C) <2009>  <Frank Scheffold (fscheffold@googlemail.com)>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "selectprofile.h"
#include "dbusinterface.h"
#include <QTreeWidgetItem>
#include <klocale.h>

SelectProfileWidget::SelectProfileWidget (QWidget *parent) : QWidget(parent) {
    selectionLabel = new QLabel();
    profilesWidget = new QTreeWidget();
    layout = new QVBoxLayout(this);
    layout->addWidget(new QLabel(i18n("Select profile")));
    profilesWidget->setHeaderLabel(i18n("Availale profiles with button actions"));
    layout->addWidget(profilesWidget);
    layout->addWidget(selectionLabel);

}

SelectProfile::SelectProfile(QString remoteName, QWidget *parent, const bool &modal): KDialog(parent)
{
    selectProfileWidget = new SelectProfileWidget;

    setMainWidget(selectProfileWidget);
    setButtons( Ok | Cancel);
    setDefaultButton(Ok);
    setModal(modal);
    setMainWidget(selectProfileWidget);
    setWindowTitle(i18n("Auto-Populate"));

    connect(selectProfileWidget->profilesWidget,SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(checkForUpdate(QTreeWidgetItem*,int)));
    kDebug()<< "remote  " << remoteName;
    QList<Profile*> profiles = ProfileServer::getInstance()->profiles();
    QStringList solidButtons = DBusInterface::getInstance()->getButtons(remoteName);
    foreach(Profile* profile, profiles) {
        ProfileServer::ProfileSupportedByRemote tSupported = ProfileServer::getInstance()->isProfileAvailableForRemote(profile, solidButtons);
	  kDebug()<< "support "<< tSupported;
        if ( tSupported != ProfileServer::NO_ACTIONS_DEFINED) {
            ProfileWrapper wrapper = ProfileWrapper(profile, tSupported);
            QTreeWidgetItem* tTreewidget = new QTreeWidgetItem(selectProfileWidget->profilesWidget,QStringList()<< profile->name());
            tTreewidget->setData(0,Qt::UserRole,qVariantFromValue<ProfileWrapper>(wrapper));
            KIcon tIcon;
            switch (tSupported) {
            case ProfileServer::FULL_SUPPORTED :
                tIcon = KIcon("flag-green");
                break;
            case ProfileServer::PARTIAL_SUPPORTED :
                tIcon = KIcon("flag-yellow");
                break;
            default:
                tIcon = KIcon("flag-red");
            }
            tTreewidget->setIcon(0, tIcon);
        }
    }
     enableButtonOk(false);
}

void SelectProfile::checkForUpdate(QTreeWidgetItem* widgetItem, int col)
{
    if (col == -1) {
        selectProfileWidget->selectionLabel->setText(QString());
        enableButtonOk(false);
        return;
    }
    ProfileServer::ProfileSupportedByRemote tSupported =  widgetItem->data(0, Qt::UserRole).value<ProfileWrapper>().getSupported();

    switch (tSupported) {
    case ProfileServer::FULL_SUPPORTED :
        selectProfileWidget->selectionLabel->setText(i18n("Remote supports all defined buttons in selected profile"));
        enableButtonOk(true);
        break;
    case ProfileServer::PARTIAL_SUPPORTED :
        selectProfileWidget->selectionLabel->setText(i18n("Remote does not support all defined buttons in selected profile"));
        enableButtonOk(true);
        break;
    case ProfileServer::NOT_SUPPORTED :
        selectProfileWidget->selectionLabel->setText(i18n("Remote supports none of the defined buttons in selected profile"));
        enableButtonOk(false);
        break;
    default:
        selectProfileWidget->selectionLabel->setText(QString());
        enableButtonOk(false);
    }
}

Profile* SelectProfile::getSelectedProfile()
{
    return selectProfileWidget->profilesWidget->currentItem()->data(0, Qt::UserRole).value<ProfileWrapper>().getProfile();
}

<commit_msg>Typo fix<commit_after>/*
    Copyright (C) <2009>  <Frank Scheffold (fscheffold@googlemail.com)>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "selectprofile.h"
#include "dbusinterface.h"
#include <QTreeWidgetItem>
#include <klocale.h>

SelectProfileWidget::SelectProfileWidget (QWidget *parent) : QWidget(parent) {
    selectionLabel = new QLabel();
    profilesWidget = new QTreeWidget();
    layout = new QVBoxLayout(this);
    layout->addWidget(new QLabel(i18n("Select profile")));
    profilesWidget->setHeaderLabel(i18n("Available profiles with button actions"));
    layout->addWidget(profilesWidget);
    layout->addWidget(selectionLabel);

}

SelectProfile::SelectProfile(QString remoteName, QWidget *parent, const bool &modal): KDialog(parent)
{
    selectProfileWidget = new SelectProfileWidget;

    setMainWidget(selectProfileWidget);
    setButtons( Ok | Cancel);
    setDefaultButton(Ok);
    setModal(modal);
    setMainWidget(selectProfileWidget);
    setWindowTitle(i18n("Auto-Populate"));

    connect(selectProfileWidget->profilesWidget,SIGNAL(itemActivated(QTreeWidgetItem*,int)), this, SLOT(checkForUpdate(QTreeWidgetItem*,int)));
    kDebug()<< "remote  " << remoteName;
    QList<Profile*> profiles = ProfileServer::getInstance()->profiles();
    QStringList solidButtons = DBusInterface::getInstance()->getButtons(remoteName);
    foreach(Profile* profile, profiles) {
        ProfileServer::ProfileSupportedByRemote tSupported = ProfileServer::getInstance()->isProfileAvailableForRemote(profile, solidButtons);
	  kDebug()<< "support "<< tSupported;
        if ( tSupported != ProfileServer::NO_ACTIONS_DEFINED) {
            ProfileWrapper wrapper = ProfileWrapper(profile, tSupported);
            QTreeWidgetItem* tTreewidget = new QTreeWidgetItem(selectProfileWidget->profilesWidget,QStringList()<< profile->name());
            tTreewidget->setData(0,Qt::UserRole,qVariantFromValue<ProfileWrapper>(wrapper));
            KIcon tIcon;
            switch (tSupported) {
            case ProfileServer::FULL_SUPPORTED :
                tIcon = KIcon("flag-green");
                break;
            case ProfileServer::PARTIAL_SUPPORTED :
                tIcon = KIcon("flag-yellow");
                break;
            default:
                tIcon = KIcon("flag-red");
            }
            tTreewidget->setIcon(0, tIcon);
        }
    }
     enableButtonOk(false);
}

void SelectProfile::checkForUpdate(QTreeWidgetItem* widgetItem, int col)
{
    if (col == -1) {
        selectProfileWidget->selectionLabel->setText(QString());
        enableButtonOk(false);
        return;
    }
    ProfileServer::ProfileSupportedByRemote tSupported =  widgetItem->data(0, Qt::UserRole).value<ProfileWrapper>().getSupported();

    switch (tSupported) {
    case ProfileServer::FULL_SUPPORTED :
        selectProfileWidget->selectionLabel->setText(i18n("Remote supports all defined buttons in selected profile"));
        enableButtonOk(true);
        break;
    case ProfileServer::PARTIAL_SUPPORTED :
        selectProfileWidget->selectionLabel->setText(i18n("Remote does not support all defined buttons in selected profile"));
        enableButtonOk(true);
        break;
    case ProfileServer::NOT_SUPPORTED :
        selectProfileWidget->selectionLabel->setText(i18n("Remote supports none of the defined buttons in selected profile"));
        enableButtonOk(false);
        break;
    default:
        selectProfileWidget->selectionLabel->setText(QString());
        enableButtonOk(false);
    }
}

Profile* SelectProfile::getSelectedProfile()
{
    return selectProfileWidget->profilesWidget->currentItem()->data(0, Qt::UserRole).value<ProfileWrapper>().getProfile();
}

<|endoftext|>


<commit_before>/*
 * This is a simple kioslave to handle mbox-files.
 * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
#include "readmbox.h"

#include "mbox.h"
#include "urlinfo.h"

#include <kdebug.h>
#include <kio/global.h>

#include <QDateTime>
#include <qfile.h>
#include <qfileinfo.h>
#include <qstring.h>
#include <qtextstream.h>

#include <utime.h>

ReadMBox::ReadMBox( const UrlInfo* info, MBoxProtocol* parent, bool onlynew, bool savetime )
	: MBoxFile( info, parent ),
	m_file( 0 ),
	m_stream( 0 ),
	m_current_line( new QString( QString::null ) ),
	m_current_id( new QString( QString::null ) ),
	m_prev_time( 0 ),
	m_only_new( onlynew ),
	m_savetime( savetime ),
	m_status( false ),
	m_prev_status( false ),
	m_header( true )

{
	if( m_info->type() == UrlInfo::invalid )
		m_mbox->emitError( KIO::ERR_DOES_NOT_EXIST, info->url() );

	if( !open( savetime ) )
		m_mbox->emitError( KIO::ERR_CANNOT_OPEN_FOR_READING, info->url() );

	if( m_info->type() == UrlInfo::message )
		if( !searchMessage( m_info->id() ) )
			m_mbox->emitError( KIO::ERR_DOES_NOT_EXIST, info->url() );
}

ReadMBox::~ReadMBox()
{
	delete m_current_line;
	close();
}

QString ReadMBox::currentLine() const
{
	return *m_current_line;
}

QString ReadMBox::currentID() const
{
	return *m_current_id;
}

bool ReadMBox::nextLine()
{
	if( !m_stream )
		return true;

	if( m_stream->atEnd() )
	{
		*m_current_line = QString::null;
		*m_current_id = QString::null;
		m_prev_status = m_status;
		return true;
	}

	*m_current_line = m_stream->readLine();

	//New message
	if( m_current_line->left( 5 ) == "From " )
	{
		*m_current_id = *m_current_line;
		m_prev_status = m_status;
		m_status = true;
		m_header = true;
		return true;
	} else if( m_only_new )
	{
		if( m_header && m_current_line->left( 7 ) == "Status:" &&
		    ! m_current_line->contains( "U" ) && ! m_current_line->contains( "N" ) )
		{
			m_status = false;
		}
	}

	if( m_current_line->trimmed().isEmpty() )
		m_header = false;

	return false;
}

bool ReadMBox::searchMessage( const QString& id )
{
	if( !m_stream )
		return false;

	while( !m_stream->atEnd() && *m_current_id != id )
		nextLine();

	return *m_current_id == id;
}

unsigned int ReadMBox::skipMessage()
{
	unsigned int result = m_current_line->length();

	if( !m_stream )
		return 0;

	while( !nextLine() )
		result += m_current_line->length();

	return result;
}

void ReadMBox::rewind()
{
	if( m_stream )
		m_stream->device()->reset();
}

bool ReadMBox::atEnd() const
{
	if( !m_stream )
		return true;

	return m_stream->atEnd() || ( m_info->type() == UrlInfo::message && *m_current_id != m_info->id() );
}

bool ReadMBox::inListing() const
{
	return !m_only_new || m_prev_status;
}

bool ReadMBox::open( bool savetime )
{
	if( savetime )
	{
		QFileInfo info( m_info->filename() );

		m_prev_time = new utimbuf;
		m_prev_time->actime = info.lastRead().toTime_t();
		m_prev_time->modtime = info.lastModified().toTime_t();
	}

	if( m_file )
		return false; //File already open

	m_file = new QFile( m_info->filename() );
	if( !m_file->open( QIODevice::ReadOnly ) )
	{
		delete m_file;
		return false;
	}
	m_stream = new QTextStream( m_file );
	skipMessage();

	return true;
}

void ReadMBox::close()
{
	if( !m_stream )
		return;

	delete m_stream; m_stream = 0;
	m_file->close();
	delete m_file; m_file = 0;

	if( m_prev_time )
		utime( QFile::encodeName( m_info->filename() ), m_prev_time );
}

<commit_msg>forward port: SVN commit 489820 by adridg:<commit_after>/*
 * This is a simple kioslave to handle mbox-files.
 * Copyright (C) 2004 Mart Kelder (mart.kde@hccnet.nl)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
#include <config.h>

#include "readmbox.h"

#include "mbox.h"
#include "urlinfo.h"

#include <kdebug.h>
#include <kio/global.h>

#include <QDateTime>
#include <qfile.h>
#include <qfileinfo.h>
#include <qstring.h>
#include <qtextstream.h>

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#include <utime.h>

ReadMBox::ReadMBox( const UrlInfo* info, MBoxProtocol* parent, bool onlynew, bool savetime )
	: MBoxFile( info, parent ),
	m_file( 0 ),
	m_stream( 0 ),
	m_current_line( new QString( QString::null ) ),
	m_current_id( new QString( QString::null ) ),
	m_prev_time( 0 ),
	m_only_new( onlynew ),
	m_savetime( savetime ),
	m_status( false ),
	m_prev_status( false ),
	m_header( true )

{
	if( m_info->type() == UrlInfo::invalid )
		m_mbox->emitError( KIO::ERR_DOES_NOT_EXIST, info->url() );

	if( !open( savetime ) )
		m_mbox->emitError( KIO::ERR_CANNOT_OPEN_FOR_READING, info->url() );

	if( m_info->type() == UrlInfo::message )
		if( !searchMessage( m_info->id() ) )
			m_mbox->emitError( KIO::ERR_DOES_NOT_EXIST, info->url() );
}

ReadMBox::~ReadMBox()
{
	delete m_current_line;
	close();
}

QString ReadMBox::currentLine() const
{
	return *m_current_line;
}

QString ReadMBox::currentID() const
{
	return *m_current_id;
}

bool ReadMBox::nextLine()
{
	if( !m_stream )
		return true;

	if( m_stream->atEnd() )
	{
		*m_current_line = QString::null;
		*m_current_id = QString::null;
		m_prev_status = m_status;
		return true;
	}

	*m_current_line = m_stream->readLine();

	//New message
	if( m_current_line->left( 5 ) == "From " )
	{
		*m_current_id = *m_current_line;
		m_prev_status = m_status;
		m_status = true;
		m_header = true;
		return true;
	} else if( m_only_new )
	{
		if( m_header && m_current_line->left( 7 ) == "Status:" &&
		    ! m_current_line->contains( "U" ) && ! m_current_line->contains( "N" ) )
		{
			m_status = false;
		}
	}

	if( m_current_line->trimmed().isEmpty() )
		m_header = false;

	return false;
}

bool ReadMBox::searchMessage( const QString& id )
{
	if( !m_stream )
		return false;

	while( !m_stream->atEnd() && *m_current_id != id )
		nextLine();

	return *m_current_id == id;
}

unsigned int ReadMBox::skipMessage()
{
	unsigned int result = m_current_line->length();

	if( !m_stream )
		return 0;

	while( !nextLine() )
		result += m_current_line->length();

	return result;
}

void ReadMBox::rewind()
{
	if( m_stream )
		m_stream->device()->reset();
}

bool ReadMBox::atEnd() const
{
	if( !m_stream )
		return true;

	return m_stream->atEnd() || ( m_info->type() == UrlInfo::message && *m_current_id != m_info->id() );
}

bool ReadMBox::inListing() const
{
	return !m_only_new || m_prev_status;
}

bool ReadMBox::open( bool savetime )
{
	if( savetime )
	{
		QFileInfo info( m_info->filename() );

		m_prev_time = new utimbuf;
		m_prev_time->actime = info.lastRead().toTime_t();
		m_prev_time->modtime = info.lastModified().toTime_t();
	}

	if( m_file )
		return false; //File already open

	m_file = new QFile( m_info->filename() );
	if( !m_file->open( QIODevice::ReadOnly ) )
	{
		delete m_file;
		return false;
	}
	m_stream = new QTextStream( m_file );
	skipMessage();

	return true;
}

void ReadMBox::close()
{
	if( !m_stream )
		return;

	delete m_stream; m_stream = 0;
	m_file->close();
	delete m_file; m_file = 0;

	if( m_prev_time )
		utime( QFile::encodeName( m_info->filename() ), m_prev_time );
}

<|endoftext|>