<commit_before>#include <x0/strutils.hpp>

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>

#include <iostream>
#include <cstdlib>
#include <cctype>
#include <cassert>
#include <strings.h>

class strutils_test :
	public CPPUNIT_NS::TestFixture
{
public:
	CPPUNIT_TEST_SUITE(strutils_test);
		CPPUNIT_TEST(parse_url_full);
		CPPUNIT_TEST(parse_url_0);
		CPPUNIT_TEST(parse_url_1);
		CPPUNIT_TEST(parse_url_2);
		CPPUNIT_TEST(parse_url_3);
		CPPUNIT_TEST(parse_url_4);
	CPPUNIT_TEST_SUITE_END();

private:
	void parse_url_full()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("http://xzero.ws:8080/path/to?query", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "http");
		CPPUNIT_ASSERT(hostname == "xzero.ws");
		CPPUNIT_ASSERT(port == 8080);
		CPPUNIT_ASSERT(path == "/path/to");
		CPPUNIT_ASSERT(query == "query");
	}

	void parse_url_0()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("http://xzero.ws", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "http");
		CPPUNIT_ASSERT(hostname == "xzero.ws");
		CPPUNIT_ASSERT(port == 80);
		CPPUNIT_ASSERT(path == "");
		CPPUNIT_ASSERT(query == "");
	}

	void parse_url_1()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("http://xzero.ws/", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "http");
		CPPUNIT_ASSERT(hostname == "xzero.ws");
		CPPUNIT_ASSERT(port == 80);
		CPPUNIT_ASSERT(path == "/");
		CPPUNIT_ASSERT(query == "");
	}

	void parse_url_2()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("https://xzero.ws/", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "https");
		CPPUNIT_ASSERT(hostname == "xzero.ws");
		CPPUNIT_ASSERT(port == 443);
		CPPUNIT_ASSERT(path == "/");
		CPPUNIT_ASSERT(query == "");
	}

	void parse_url_3()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("http://xzero.ws/?query", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "http");
		CPPUNIT_ASSERT(hostname == "xzero.ws");
		CPPUNIT_ASSERT(port == 80);
		CPPUNIT_ASSERT(path == "/");
		CPPUNIT_ASSERT(query == "query");
	}

	void parse_url_4()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("http://::1:8080/", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "http");
		CPPUNIT_ASSERT(hostname == "::1");
		CPPUNIT_ASSERT(port == 8080);
		CPPUNIT_ASSERT(path == "/");
		CPPUNIT_ASSERT(query == "");
	}
};

CPPUNIT_TEST_SUITE_REGISTRATION(strutils_test);
<commit_msg>adapted to changes in url api (header change)<commit_after>#include <x0/strutils.hpp>
#include <x0/url.hpp>

#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>

#include <iostream>
#include <cstdlib>
#include <cctype>
#include <cassert>
#include <strings.h>

class strutils_test :
	public CPPUNIT_NS::TestFixture
{
public:
	CPPUNIT_TEST_SUITE(strutils_test);
		CPPUNIT_TEST(parse_url_full);
		CPPUNIT_TEST(parse_url_0);
		CPPUNIT_TEST(parse_url_1);
		CPPUNIT_TEST(parse_url_2);
		CPPUNIT_TEST(parse_url_3);
		CPPUNIT_TEST(parse_url_4);
	CPPUNIT_TEST_SUITE_END();

private:
	void parse_url_full()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("http://xzero.ws:8080/path/to?query", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "http");
		CPPUNIT_ASSERT(hostname == "xzero.ws");
		CPPUNIT_ASSERT(port == 8080);
		CPPUNIT_ASSERT(path == "/path/to");
		CPPUNIT_ASSERT(query == "query");
	}

	void parse_url_0()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("http://xzero.ws", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "http");
		CPPUNIT_ASSERT(hostname == "xzero.ws");
		CPPUNIT_ASSERT(port == 80);
		CPPUNIT_ASSERT(path == "");
		CPPUNIT_ASSERT(query == "");
	}

	void parse_url_1()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("http://xzero.ws/", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "http");
		CPPUNIT_ASSERT(hostname == "xzero.ws");
		CPPUNIT_ASSERT(port == 80);
		CPPUNIT_ASSERT(path == "/");
		CPPUNIT_ASSERT(query == "");
	}

	void parse_url_2()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("https://xzero.ws/", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "https");
		CPPUNIT_ASSERT(hostname == "xzero.ws");
		CPPUNIT_ASSERT(port == 443);
		CPPUNIT_ASSERT(path == "/");
		CPPUNIT_ASSERT(query == "");
	}

	void parse_url_3()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("http://xzero.ws/?query", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "http");
		CPPUNIT_ASSERT(hostname == "xzero.ws");
		CPPUNIT_ASSERT(port == 80);
		CPPUNIT_ASSERT(path == "/");
		CPPUNIT_ASSERT(query == "query");
	}

	void parse_url_4()
	{
		std::string protocol, hostname, path, query;
		int port = -1;

		bool rv = x0::parse_url("http://::1:8080/", protocol, hostname, port, path, query);

		CPPUNIT_ASSERT(rv == true);
		CPPUNIT_ASSERT(protocol == "http");
		CPPUNIT_ASSERT(hostname == "::1");
		CPPUNIT_ASSERT(port == 8080);
		CPPUNIT_ASSERT(path == "/");
		CPPUNIT_ASSERT(query == "");
	}
};

CPPUNIT_TEST_SUITE_REGISTRATION(strutils_test);
<|endoftext|>


<commit_before>#include "KVMainWindow.h"
#include <QMenuBar>
#include <QMenu>
#include <QWebFrame>
#include <QInputDialog>
#include <QFile>
#include <QUrl>
#include <QUrlQuery>
#include <QApplication>
#include <QNetworkProxy>
#include <QNetworkReply>
#include <QHostAddress>
#include <QSettings>
#include <QStandardPaths>
#include <QDebug>
#include "KVTranslator.h"

KVMainWindow::KVMainWindow(QWidget *parent, Qt::WindowFlags flags):
	QMainWindow(parent, flags)
{
	this->setWindowTitle("KanColleTool Viewer");

	// Set up the window and menus and stuff
	QMenuBar *menuBar = new QMenuBar(this);

	QMenu *viewerMenu = menuBar->addMenu("Viewer");
	viewerMenu->addAction("Change API Link", this, SLOT(askForAPILink()))->setShortcut(Qt::CTRL + Qt::Key_L);
	viewerMenu->addAction("Clear Cache", this, SLOT(clearCache()));
	viewerMenu->addSeparator();
	viewerMenu->addAction("Quit", qApp, SLOT(quit()))->setShortcut(Qt::CTRL + Qt::Key_Q);

	QMenu *helpMenu = menuBar->addMenu("Help");
	helpMenu->addAction("About", this, SLOT(showAbout()));
	// Updates on Linux are handled by the package manager
#if !defined(Q_OS_LINUX)
	helpMenu->addAction("Check for Updates", this, SLOT(checkForUpdates()));
#endif

	this->setMenuBar(menuBar);

	// Set a custom network access manager to let us set up a cache and proxy.
	// Without a cache, the game takes ages to load.
	// Without a proxy, we can't do cool things like translating the game.
	wvManager = new QNetworkAccessManager(this);

	// Set up a cache; a larger-than-normal disk cache is quite enough for our purposes
	cache = new QNetworkDiskCache(this);
	cache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
	wvManager->setCache(cache);

	// Set up a local proxy
	proxy = new KVProxy(this);
	proxy->run();
	wvManager->setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxy->port()));

	//connect(proxy, SIGNAL(apiError(KVProxyServer::APIStatus)), this, SLOT(onAPIError(KVProxyServer::APIStatus)));

	// Set up the web view, using our custom Network Access Manager
	webView = new QWebView(this);
	webView->page()->setNetworkAccessManager(wvManager);

	// The context menu only contains "Reload" anyways
	webView->setContextMenuPolicy(Qt::PreventContextMenu);
	// These are so large that they create a need for themselves >_>
	webView->page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
	webView->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);

	connect(webView, SIGNAL(loadStarted()), this, SLOT(onLoadStarted()));
	connect(webView, SIGNAL(loadFinished(bool)), this, SLOT(onLoadFinished(bool)));

	// To set the window size right, we have to first set the central widget (which we set to
	// the web view)'s size to a fixed size, ask the window to auto-adjust to that, and then
	// fix its size to what it adjusted itself to. A bit clumsy, but it works, and won't stop
	// us from adding any additional elements to the window, as this will account for ANYTHING.
	this->setCentralWidget(webView);
	this->centralWidget()->setFixedSize(800, 480);
	this->adjustSize();
	this->setFixedSize(this->width(), this->height());

	// Check for updates
	// Updates on Linux are handled by the package manager
#if !defined(Q_OS_LINUX)
	this->checkForUpdates();
#endif

	// Load the translation data before doing anything, otherwise we might end
	// up with partially translated data on spotty connections.
	this->loadTranslation();

	// Ask for an API Link if we don't have one already, otherwise just restore it
	//this->loadAPILink();

	// Load the bundled index.html file
	//this->loadBundledIndex();
}

void KVMainWindow::checkForUpdates()
{
	QNetworkReply *reply = manager.get(QNetworkRequest(QUrl("http://kancolletool.github.io/VERSION")));
	connect(reply, SIGNAL(finished()), this, SLOT(onVersionCheckFinished()));
}

void KVMainWindow::loadTranslation(QString language)
{
	loadingMessageBox = new QMessageBox(QMessageBox::NoIcon, "Loading translation...", "This should only take a moment.", QMessageBox::Cancel, this);
	QTimer::singleShot(0, loadingMessageBox, SLOT(open()));

	KVTranslator *translator = KVTranslator::instance();
	connect(translator, SIGNAL(loadFinished()), this, SLOT(onTranslationLoadFinished()));
	connect(translator, SIGNAL(loadFailed(QString)), this, SLOT(onTranslationLoadFailed(QString)));
	translator->loadTranslation(language);
}

void KVMainWindow::loadBundledIndex()
{
	QFile file(":/index.html");
	if(file.open(QIODevice::ReadOnly))
	{
		webView->setHtml(file.readAll(), apiLink);
	}
	else
	{
		QMessageBox::critical(this, "Can't load resource", "Couldn't load the local resources needed to start the client.

I have no idea how you even managed to make this happen, since the resources are supposed to be inside the executable, but it probably involved a recompilation that went wrong.

index.html needs to be in the root of resources.qrc.");
		exit(1);
	}
}

void KVMainWindow::loadAPILink()
{
	QSettings settings;

	server = settings.value("server").toString();
	apiToken = settings.value("apiToken").toString();

	if(server.isEmpty() || apiToken.isEmpty())
	{
		this->askForAPILink(false);
		if(server.isEmpty() || apiToken.isEmpty())
			exit(0);
	}
	else this->generateAPILinkURL();

	qDebug() << "Server:" << server;
	qDebug() << "API Token:" << apiToken;
	qDebug() << "API Link:" << apiLink.toString();
}

void KVMainWindow::generateAPILinkURL()
{
	apiLink = QUrl(QString("http://%1/kcs/mainD2.swf?api_token=%2").arg(server, apiToken));
}

void KVMainWindow::askForAPILink(bool reload)
{
	// Get the link from the user
	QString link = QInputDialog::getText(this, "Enter API Link", "Please enter your API Link.

It should look something like:
http://125.6.XXX.XXX/kcs/mainD2.swf?api_token=xxxxxxxxxx...");

	// If the link is empty, the user pressed cancel, for whatever reason
	if(link.isEmpty())
		return;

	// Make an URL and a Query from it
	QUrl url(link);
	QUrlQuery query(url);

	// Extract the important bits, and generate a well-formed URL from that
	// (It's important that nothing we're doing is noticeable to the staff!)
	server = url.host();
	apiToken = query.queryItemValue("api_token");
	this->generateAPILinkURL();

	// Put it in the settings and force a sync
	QSettings settings;
	settings.setValue("server", server);
	settings.setValue("apiToken", apiToken);
	settings.sync();

	// If we're in-game and expected to reload the page, do so.
	// This is NOT true when called from loadAPILink(), and should
	// be true when called from the menu item.
	if(reload)
		this->loadBundledIndex();
}

void KVMainWindow::clearCache()
{
	cache->clear();
	this->loadBundledIndex();
}

void KVMainWindow::showAbout()
{
	QMessageBox::about(this, "About KCTViewer",
		QString(
			"
KCTViewer %1
"
		).arg(
			QCoreApplication::applicationVersion()
		)
	);
}

void KVMainWindow::onVersionCheckFinished()
{
	QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());

	// If the check failed, do nothing
	if(reply->error() != QNetworkReply::NoError)
		return;

	// Parse the version numbers
	QString newVersion = QString::fromUtf8(reply->readAll()).trimmed();
	QList<int> newVersionComponents;
	foreach(QString part, newVersion.split("."))
		newVersionComponents.append(part.toInt());

	QString appVersion = qApp->applicationVersion();
	QList<int> appVersionComponents;
	foreach(QString part, appVersion.split("."))
		appVersionComponents.append(part.toInt());

	// Compare component-per-component to see if we're outdated
	bool outdated = false;
	for(int i = 0; i < qMin(newVersionComponents.length(), appVersionComponents.length()); i++)
	{
		if(newVersionComponents[i] != appVersionComponents[i])
		{
			outdated = (newVersionComponents[i] > appVersionComponents[i]);
			break;
		}
	}

	// Display a message if we are
	if(outdated)
		QMessageBox::information(this, "New Version Available", "Version " + newVersion + " has been released, and is available at:
http://kancolletool.github.io/downloads/");
}

void KVMainWindow::onLoadStarted()
{
	qDebug() << "Loading Started...";
}

void KVMainWindow::onLoadFinished(bool ok)
{
	qDebug() << "Finished Loading!" << ok;
	if(ok) this->setHTMLAPILink();
}

void KVMainWindow::onTranslationLoadFinished()
{
	qDebug() << "Translation finished loading!";
	loadingMessageBox->accept();
	delete loadingMessageBox;

	this->loadAPILink();
	this->loadBundledIndex();
}

void KVMainWindow::onTranslationLoadFailed(QString error)
{
	qDebug() << "Translation failed to load:" << error;
	loadingMessageBox->reject();
	delete loadingMessageBox;

	QMessageBox::StandardButton button = QMessageBox::warning(this, "Couldn't load translation", "This might mean that your connection is bad. You can continue without translation, but the game will be in Japanese.", QMessageBox::Retry|QMessageBox::Ok, QMessageBox::Ok);

	// To retry, just send the request again.
	if(button == QMessageBox::Retry)
		this->loadTranslation();

	// To ignore it, just pretend like it succeeded.
	else
		this->onTranslationLoadFinished();
}

void KVMainWindow::setHTMLAPILink()
{
	qDebug() << "Updating API Link in the web view to" << apiLink.toString();
	webView->page()->mainFrame()->evaluateJavaScript(QString("setAPILink(\"%1\"); null").arg(apiLink.toString()));
}

/*void KVMainWindow::onAPIError(KVProxyServer::APIStatus error)
{
	qDebug() << error;

	QString readableError;

	switch(error)
	{
		case KVProxyServer::OK:
			readableError = "No Error, you shouldn't be seeing this.";
			break;
		case KVProxyServer::Unauthorized:
			readableError = "Either your API Link is invalid, or it has expired. That happens sometimes.";
			break;
		case KVProxyServer::InvalidAPIVersion:
		case KVProxyServer::MissingParameters:
			readableError = "The game was updated, but you have an old client.";
			break;
		default:
			readableError = "An unknown error occurred. Please tell the developers that this happened ALONG WITH THE ERROR CODE, and what you think caused it.";
	}

	QMessageBox::critical(this, QString("Errorcat (Code %1)").arg((int)error), readableError);
}*/
<commit_msg>Also do it in the Qt viewer<commit_after>#include "KVMainWindow.h"
#include <QMenuBar>
#include <QMenu>
#include <QWebFrame>
#include <QInputDialog>
#include <QFile>
#include <QUrl>
#include <QUrlQuery>
#include <QApplication>
#include <QNetworkProxy>
#include <QNetworkReply>
#include <QHostAddress>
#include <QSettings>
#include <QStandardPaths>
#include <QDebug>
#include "KVTranslator.h"

KVMainWindow::KVMainWindow(QWidget *parent, Qt::WindowFlags flags):
	QMainWindow(parent, flags)
{
	this->setWindowTitle("KanColleTool Viewer");

	// Set up the window and menus and stuff
	QMenuBar *menuBar = new QMenuBar(this);

	QMenu *viewerMenu = menuBar->addMenu("Viewer");
	viewerMenu->addAction("Change API Link", this, SLOT(askForAPILink()))->setShortcut(Qt::CTRL + Qt::Key_L);
	viewerMenu->addAction("Clear Cache", this, SLOT(clearCache()));
	viewerMenu->addSeparator();
	viewerMenu->addAction("Quit", qApp, SLOT(quit()))->setShortcut(Qt::CTRL + Qt::Key_Q);

	QMenu *helpMenu = menuBar->addMenu("Help");
	helpMenu->addAction("About", this, SLOT(showAbout()));
	// Updates on Linux are handled by the package manager
#if !defined(Q_OS_LINUX)
	helpMenu->addAction("Check for Updates", this, SLOT(checkForUpdates()));
#endif

	this->setMenuBar(menuBar);

	// Set a custom network access manager to let us set up a cache and proxy.
	// Without a cache, the game takes ages to load.
	// Without a proxy, we can't do cool things like translating the game.
	wvManager = new QNetworkAccessManager(this);

	// Set up a cache; a larger-than-normal disk cache is quite enough for our purposes
	cache = new QNetworkDiskCache(this);
	cache->setCacheDirectory(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
	wvManager->setCache(cache);

	// Set up a local proxy
	proxy = new KVProxy(this);
	proxy->run();
	wvManager->setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxy->port()));

	//connect(proxy, SIGNAL(apiError(KVProxyServer::APIStatus)), this, SLOT(onAPIError(KVProxyServer::APIStatus)));

	// Set up the web view, using our custom Network Access Manager
	webView = new QWebView(this);
	webView->page()->setNetworkAccessManager(wvManager);

	// The context menu only contains "Reload" anyways
	webView->setContextMenuPolicy(Qt::PreventContextMenu);
	// These are so large that they create a need for themselves >_>
	webView->page()->mainFrame()->setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff);
	webView->page()->mainFrame()->setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff);

	connect(webView, SIGNAL(loadStarted()), this, SLOT(onLoadStarted()));
	connect(webView, SIGNAL(loadFinished(bool)), this, SLOT(onLoadFinished(bool)));

	// To set the window size right, we have to first set the central widget (which we set to
	// the web view)'s size to a fixed size, ask the window to auto-adjust to that, and then
	// fix its size to what it adjusted itself to. A bit clumsy, but it works, and won't stop
	// us from adding any additional elements to the window, as this will account for ANYTHING.
	this->setCentralWidget(webView);
	this->centralWidget()->setFixedSize(800, 480);
	this->adjustSize();
	this->setFixedSize(this->width(), this->height());

	// Check for updates
	// Updates on Linux are handled by the package manager
#if !defined(Q_OS_LINUX)
	this->checkForUpdates();
#endif

	// Load the translation data before doing anything, otherwise we might end
	// up with partially translated data on spotty connections.
	this->loadTranslation();

	// Ask for an API Link if we don't have one already, otherwise just restore it
	//this->loadAPILink();

	// Load the bundled index.html file
	//this->loadBundledIndex();
}

void KVMainWindow::checkForUpdates()
{
	QNetworkReply *reply = manager.get(QNetworkRequest(QUrl("http://kancolletool.github.io/VERSION")));
	connect(reply, SIGNAL(finished()), this, SLOT(onVersionCheckFinished()));
}

void KVMainWindow::loadTranslation(QString language)
{
	loadingMessageBox = new QMessageBox(QMessageBox::NoIcon, "Loading translation...", "This should only take a moment.", QMessageBox::Cancel, this);
	QTimer::singleShot(0, loadingMessageBox, SLOT(open()));

	KVTranslator *translator = KVTranslator::instance();
	connect(translator, SIGNAL(loadFinished()), this, SLOT(onTranslationLoadFinished()));
	connect(translator, SIGNAL(loadFailed(QString)), this, SLOT(onTranslationLoadFailed(QString)));
	translator->loadTranslation(language);
}

void KVMainWindow::loadBundledIndex()
{
	QFile file(":/index.html");
	if(file.open(QIODevice::ReadOnly))
	{
		webView->setHtml(file.readAll(), apiLink);
	}
	else
	{
		QMessageBox::critical(this, "Can't load resource", "Couldn't load the local resources needed to start the client.

I have no idea how you even managed to make this happen, since the resources are supposed to be inside the executable, but it probably involved a recompilation that went wrong.

index.html needs to be in the root of resources.qrc.");
		exit(1);
	}
}

void KVMainWindow::loadAPILink()
{
	QSettings settings;

	server = settings.value("server").toString();
	apiToken = settings.value("apiToken").toString();

	if(server.isEmpty() || apiToken.isEmpty())
	{
		this->askForAPILink(false);
		if(server.isEmpty() || apiToken.isEmpty())
			exit(0);
	}
	else this->generateAPILinkURL();

	qDebug() << "Server:" << server;
	qDebug() << "API Token:" << apiToken;
	qDebug() << "API Link:" << apiLink.toString();
}

void KVMainWindow::generateAPILinkURL()
{
	apiLink = QUrl(QString("http://%1/kcs/mainD2.swf?api_token=%2").arg(server, apiToken));
}

void KVMainWindow::askForAPILink(bool reload)
{
	// Get the link from the user
	QString link = QInputDialog::getText(this, "Enter API Link", "Please enter your API Link.

It should look something like:
http://125.6.XXX.XXX/kcs/mainD2.swf?api_token=xxxxxxxxxx...");

	// If the link is empty, the user pressed cancel, for whatever reason
	if(link.isEmpty())
		return;

	// Make an URL and a Query from it
	QUrl url(link);
	QUrlQuery query(url);

	// Extract the important bits, and generate a well-formed URL from that
	// (It's important that nothing we're doing is noticeable to the staff!)
	server = url.host();
	apiToken = query.queryItemValue("api_token");
	this->generateAPILinkURL();

	// Put it in the settings and force a sync
	QSettings settings;
	settings.setValue("server", server);
	settings.setValue("apiToken", apiToken);
	settings.sync();

	// If we're in-game and expected to reload the page, do so.
	// This is NOT true when called from loadAPILink(), and should
	// be true when called from the menu item.
	if(reload)
		this->loadBundledIndex();
}

void KVMainWindow::clearCache()
{
	cache->clear();
	this->loadBundledIndex();
}

void KVMainWindow::showAbout()
{
	QMessageBox::about(this, "About KCTViewer",
		QString(
			"
KCTViewer %1
"
		).arg(
			QCoreApplication::applicationVersion()
		)
	);
}

void KVMainWindow::onVersionCheckFinished()
{
	QNetworkReply *reply = qobject_cast<QNetworkReply*>(QObject::sender());

	// If the check failed, do nothing
	if(reply->error() != QNetworkReply::NoError)
		return;

	// Parse the version numbers
	QString newVersion = QString::fromUtf8(reply->readAll()).trimmed();
	QList<int> newVersionComponents;
	foreach(QString part, newVersion.split("."))
		newVersionComponents.append(part.toInt());

	QString appVersion = qApp->applicationVersion();
	QList<int> appVersionComponents;
	foreach(QString part, appVersion.split("."))
		appVersionComponents.append(part.toInt());

	// Compare component-per-component to see if we're outdated
	bool outdated = false;
	for(int i = 0; i < qMin(newVersionComponents.length(), appVersionComponents.length()); i++)
	{
		if(newVersionComponents[i] != appVersionComponents[i])
		{
			outdated = (newVersionComponents[i] > appVersionComponents[i]);
			break;
		}
	}

	// Display a message if we are
	if(outdated)
		QMessageBox::information(this, "New Version Available", "Version " + newVersion + " has been released, and is available at:
http://kancolletool.github.io/downloads/");
}

void KVMainWindow::onLoadStarted()
{
	qDebug() << "Loading Started...";
}

void KVMainWindow::onLoadFinished(bool ok)
{
	qDebug() << "Finished Loading!" << ok;
	if(ok) this->setHTMLAPILink();
}

void KVMainWindow::onTranslationLoadFinished()
{
	qDebug() << "Translation finished loading!";
	loadingMessageBox->accept();
	delete loadingMessageBox;

	this->loadAPILink();
	this->loadBundledIndex();
}

void KVMainWindow::onTranslationLoadFailed(QString error)
{
	qDebug() << "Translation failed to load:" << error;
	loadingMessageBox->reject();
	delete loadingMessageBox;

	QMessageBox::StandardButton button = QMessageBox::warning(this, "Couldn't load translation", "This might mean that your connection is bad. You can continue without translation, but the game will be in Japanese.", QMessageBox::Retry|QMessageBox::Ok, QMessageBox::Ok);

	// To retry, just send the request again.
	if(button == QMessageBox::Retry)
		this->loadTranslation();

	// To ignore it, just pretend like it succeeded.
	else
		this->onTranslationLoadFinished();
}

void KVMainWindow::setHTMLAPILink()
{
	qDebug() << "Updating web view credentials to" << server << "-" << apiToken;
	webView->page()->mainFrame()->evaluateJavaScript(QString("setCredentials(\"%1\", \"%2\"); null").arg(server, apiToken));
}

/*void KVMainWindow::onAPIError(KVProxyServer::APIStatus error)
{
	qDebug() << error;

	QString readableError;

	switch(error)
	{
		case KVProxyServer::OK:
			readableError = "No Error, you shouldn't be seeing this.";
			break;
		case KVProxyServer::Unauthorized:
			readableError = "Either your API Link is invalid, or it has expired. That happens sometimes.";
			break;
		case KVProxyServer::InvalidAPIVersion:
		case KVProxyServer::MissingParameters:
			readableError = "The game was updated, but you have an old client.";
			break;
		default:
			readableError = "An unknown error occurred. Please tell the developers that this happened ALONG WITH THE ERROR CODE, and what you think caused it.";
	}

	QMessageBox::critical(this, QString("Errorcat (Code %1)").arg((int)error), readableError);
}*/
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/events/event.h"

#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#if defined(HAVE_XINPUT2)
#include <X11/extensions/XInput2.h>
#endif
#include <X11/Xlib.h>

#include "base/utf_string_conversions.h"
#include "ui/base/keycodes/keyboard_code_conversion_x.h"
#include "views/widget/root_view.h"
#include "views/widget/widget_gtk.h"

#if defined(HAVE_XINPUT2)
#include "views/touchui/touch_factory.h"
#endif

namespace views {

namespace {

// Scroll amount for each wheelscroll event. 53 is also the value used for GTK+.
static int kWheelScrollAmount = 53;

int GetEventFlagsFromXState(unsigned int state) {
  int flags = 0;
  if (state & ControlMask)
    flags |= ui::EF_CONTROL_DOWN;
  if (state & ShiftMask)
    flags |= ui::EF_SHIFT_DOWN;
  if (state & Mod1Mask)
    flags |= ui::EF_ALT_DOWN;
  if (state & LockMask)
    flags |= ui::EF_CAPS_LOCK_DOWN;
  if (state & Button1Mask)
    flags |= ui::EF_LEFT_BUTTON_DOWN;
  if (state & Button2Mask)
    flags |= ui::EF_MIDDLE_BUTTON_DOWN;
  if (state & Button3Mask)
    flags |= ui::EF_RIGHT_BUTTON_DOWN;

  return flags;
}

// Get the event flag for the button in XButtonEvent. During a ButtonPress
// event, |state| in XButtonEvent does not include the button that has just been
// pressed. Instead |state| contains flags for the buttons (if any) that had
// already been pressed before the current button, and |button| stores the most
// current pressed button. So, if you press down left mouse button, and while
// pressing it down, press down the right mouse button, then for the latter
// event, |state| would have Button1Mask set but not Button3Mask, and |button|
// would be 3.
int GetEventFlagsForButton(int button) {
  switch (button) {
    case 1:
      return ui::EF_LEFT_BUTTON_DOWN;
    case 2:
      return ui::EF_MIDDLE_BUTTON_DOWN;
    case 3:
      return ui::EF_RIGHT_BUTTON_DOWN;
  }

  DLOG(WARNING) << "Unexpected button (" << button << ") received.";
  return 0;
}

#if defined(HAVE_XINPUT2)
int GetButtonMaskForX2Event(XIDeviceEvent* xievent) {
  int buttonflags = 0;

  for (int i = 0; i < 8 * xievent->buttons.mask_len; i++) {
    if (XIMaskIsSet(xievent->buttons.mask, i)) {
      buttonflags |= GetEventFlagsForButton(i);
    }
  }

  return buttonflags;
}

ui::EventType GetTouchEventType(XEvent* xev) {
  XGenericEventCookie* cookie = &xev->xcookie;
  DCHECK_EQ(cookie->evtype, ui::ET_TOUCH_MOVED);

  // Note: We will not generate a _STATIONARY event here. It will be created,
  // when necessary, by a RWHVV.
  // TODO(sad): When should _CANCELLED be generated?

  TouchFactory* factory = TouchFactory::GetInstance();
  float slot;
  if (!factory->ExtractTouchParam(*xev, TouchFactory::TP_SLOT_ID, &slot))
    return ui::ET_UNKNOWN;

  if (!factory->IsSlotUsed(slot)) {
    // This is a new touch point.
    return ui::ET_TOUCH_PRESSED;
  }

  float tracking;
  if (!factory->ExtractTouchParam(*xev, TouchFactory::TP_TRACKING_ID,
        &tracking))
    return ui::ET_UNKNOWN;

  if (tracking == 0l) {
    // The touch point has been released.
    return ui::ET_TOUCH_RELEASED;
  }

  return ui::ET_TOUCH_MOVED;
}

int GetTouchIDFromXEvent(XEvent* xev) {
  float slot = 0;
  if (!TouchFactory::GetInstance()->ExtractTouchParam(
        *xev, TouchFactory::TP_SLOT_ID, &slot))
    LOG(ERROR) << "Could not get the slot ID for the event. Using 0.";
  return slot;
}

#endif  // HAVE_XINPUT2

ui::EventType EventTypeFromNative(NativeEvent2 native_event) {
  switch (native_event->type) {
    case KeyPress:
      return ui::ET_KEY_PRESSED;
    case KeyRelease:
      return ui::ET_KEY_RELEASED;
    case ButtonPress:
      if (native_event->xbutton.button == 4 ||
          native_event->xbutton.button == 5)
        return ui::ET_MOUSEWHEEL;
      return ui::ET_MOUSE_PRESSED;
    case ButtonRelease:
      if (native_event->xbutton.button == 4 ||
          native_event->xbutton.button == 5)
        return ui::ET_MOUSEWHEEL;
      return ui::ET_MOUSE_RELEASED;
    case MotionNotify:
      if (native_event->xmotion.state &
          (Button1Mask | Button2Mask | Button3Mask))
        return ui::ET_MOUSE_DRAGGED;
      return ui::ET_MOUSE_MOVED;
#if defined(HAVE_XINPUT2)
    case GenericEvent: {
      XIDeviceEvent* xievent =
          static_cast<XIDeviceEvent*>(native_event->xcookie.data);
      if (TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid))
        return GetTouchEventType(native_event);
      switch (xievent->evtype) {
        case XI_ButtonPress:
          return (xievent->detail == 4 || xievent->detail == 5) ?
              ui::ET_MOUSEWHEEL : ui::ET_MOUSE_PRESSED;
        case XI_ButtonRelease:
          return (xievent->detail == 4 || xievent->detail == 5) ?
              ui::ET_MOUSEWHEEL : ui::ET_MOUSE_RELEASED;
        case XI_Motion:
          return GetButtonMaskForX2Event(xievent) ? ui::ET_MOUSE_DRAGGED :
              ui::ET_MOUSE_MOVED;
      }
    }
#endif
    default:
      NOTREACHED();
      break;
  }
  return ui::ET_UNKNOWN;
}

int GetMouseWheelOffset(XEvent* xev) {
#if defined(HAVE_XINPUT2)
  if (xev->type == GenericEvent) {
    XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev->xcookie.data);
    return xiev->detail == 4 ? kWheelScrollAmount : -kWheelScrollAmount;
  }
#endif
  return xev->xbutton.button == 4 ? kWheelScrollAmount : -kWheelScrollAmount;
}

gfx::Point GetEventLocation(XEvent* xev) {
  switch (xev->type) {
    case ButtonPress:
    case ButtonRelease:
      return gfx::Point(xev->xbutton.x, xev->xbutton.y);

    case MotionNotify:
      return gfx::Point(xev->xmotion.x, xev->xmotion.y);

#if defined(HAVE_XINPUT2)
    case GenericEvent: {
      XIDeviceEvent* xievent =
          static_cast<XIDeviceEvent*>(xev->xcookie.data);
      return gfx::Point(static_cast<int>(xievent->event_x),
                        static_cast<int>(xievent->event_y));
    }
#endif
  }

  return gfx::Point();
}

int GetLocatedEventFlags(XEvent* xev) {
  switch (xev->type) {
    case ButtonPress:
    case ButtonRelease:
      return GetEventFlagsFromXState(xev->xbutton.state) |
             GetEventFlagsForButton(xev->xbutton.button);

    case MotionNotify:
      return GetEventFlagsFromXState(xev->xmotion.state);

#if defined(HAVE_XINPUT2)
    case GenericEvent: {
      XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data);
      bool touch =
          TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid);
      switch (xievent->evtype) {
        case XI_ButtonPress:
        case XI_ButtonRelease:
          return GetButtonMaskForX2Event(xievent) |
                 GetEventFlagsFromXState(xievent->mods.effective) |
                 (touch ? 0 : GetEventFlagsForButton(xievent->detail));

        case XI_Motion:
           return GetButtonMaskForX2Event(xievent) |
                  GetEventFlagsFromXState(xievent->mods.effective);
      }
    }
#endif
  }

  return 0;
}

uint16 GetCharacterFromXKeyEvent(XKeyEvent* key) {
  char buf[6];
  int bytes_written = XLookupString(key, buf, 6, NULL, NULL);
  DCHECK_LE(bytes_written, 6);

  string16 result;
  return (bytes_written > 0 && UTF8ToUTF16(buf, bytes_written, &result) &&
          result.length() == 1) ? result[0] : 0;
}

float GetTouchRadiusFromXEvent(XEvent* xev) {
  float diameter = 0.0;

#if defined(HAVE_XINPUT2)
  TouchFactory* touch_factory = TouchFactory::GetInstance();
  touch_factory->ExtractTouchParam(*xev, TouchFactory::TP_TOUCH_MAJOR,
                                   &diameter);
#endif

  return diameter / 2.0;
}

float GetTouchAngleFromXEvent(XEvent* xev) {
  float angle = 0.0;

#if defined(HAVE_XINPUT2)
  TouchFactory* touch_factory = TouchFactory::GetInstance();
  touch_factory->ExtractTouchParam(*xev, TouchFactory::TP_ORIENTATION,
                                   &angle);
#endif

  return angle;
}

float GetTouchRatioFromXEvent(XEvent* xev) {
  float ratio = 1.0;

#if defined(HAVE_XINPUT2)
  TouchFactory* touch_factory = TouchFactory::GetInstance();
  float major_v = -1.0;
  float minor_v = -1.0;

  if (!touch_factory->ExtractTouchParam(*xev,
                                        TouchFactory::TP_TOUCH_MAJOR,
                                        &major_v) ||
      !touch_factory->ExtractTouchParam(*xev,
                                        TouchFactory::TP_TOUCH_MINOR,
                                        &minor_v))
    return ratio;

  // In case minor axis exists but is zero.
  if (minor_v > 0.0)
    ratio = major_v / minor_v;
#endif

  return ratio;
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// Event, private:

void Event::InitWithNativeEvent2(NativeEvent2 native_event_2,
                                 FromNativeEvent2) {
  native_event_ = NULL;
  // TODO(beng): remove once we rid views of Gtk/Gdk.
  native_event_2_ = native_event_2;
}

////////////////////////////////////////////////////////////////////////////////
// LocatedEvent, protected:

LocatedEvent::LocatedEvent(NativeEvent2 native_event_2,
                           FromNativeEvent2 from_native)
    : Event(native_event_2,
            EventTypeFromNative(native_event_2),
            GetLocatedEventFlags(native_event_2),
            from_native),
      location_(GetEventLocation(native_event_2)) {
}

////////////////////////////////////////////////////////////////////////////////
// KeyEvent, public:

KeyEvent::KeyEvent(NativeEvent2 native_event_2, FromNativeEvent2 from_native)
    : Event(native_event_2,
            EventTypeFromNative(native_event_2),
            GetEventFlagsFromXState(native_event_2->xkey.state),
            from_native),
      key_code_(ui::KeyboardCodeFromXKeyEvent(native_event_2)) {
}

uint16 KeyEvent::GetCharacter() const {
  if (!native_event_2())
    return GetCharacterFromKeyCode(key_code_, flags());

  DCHECK(native_event_2()->type == KeyPress ||
         native_event_2()->type == KeyRelease);

  uint16 ch = GetCharacterFromXKeyEvent(&native_event_2()->xkey);
  return ch ? ch : GetCharacterFromKeyCode(key_code_, flags());
}

uint16 KeyEvent::GetUnmodifiedCharacter() const {
  if (!native_event_2())
    return GetCharacterFromKeyCode(key_code_, flags() & ui::EF_SHIFT_DOWN);

  DCHECK(native_event_2()->type == KeyPress ||
         native_event_2()->type == KeyRelease);

  XKeyEvent key = native_event_2()->xkey;

  static const unsigned int kIgnoredModifiers = ControlMask | LockMask |
      Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask;

  // We can't use things like (key.state & ShiftMask), as it may mask out bits
  // used by X11 internally.
  key.state &= ~kIgnoredModifiers;
  uint16 ch = GetCharacterFromXKeyEvent(&key);
  return ch ? ch :
      GetCharacterFromKeyCode(key_code_, flags() & ui::EF_SHIFT_DOWN);
}

////////////////////////////////////////////////////////////////////////////////
// MouseEvent, public:

MouseEvent::MouseEvent(NativeEvent2 native_event_2,
                       FromNativeEvent2 from_native)
    : LocatedEvent(native_event_2, from_native) {
}

MouseEvent::MouseEvent(const TouchEvent& touch,
                       FromNativeEvent2 from_native)
    : LocatedEvent(touch.native_event_2(), from_native) {
  // The location of the event is correctly extracted from the native event. But
  // it is necessary to update the event type.
  ui::EventType mtype = ui::ET_UNKNOWN;
  switch (touch.type()) {
    case ui::ET_TOUCH_RELEASED:
      mtype = ui::ET_MOUSE_RELEASED;
      break;
    case ui::ET_TOUCH_PRESSED:
      mtype = ui::ET_MOUSE_PRESSED;
      break;
    case ui::ET_TOUCH_MOVED:
      mtype = ui::ET_MOUSE_MOVED;
      break;
    default:
      NOTREACHED() << "Invalid mouse event.";
  }
  set_type(mtype);

  // It may not be possible to extract the button-information necessary for a
  // MouseEvent from the native event for a TouchEvent, so the flags are
  // explicitly updated as well. The button is approximated from the touchpoint
  // identity.
  int new_flags = flags() & ~(ui::EF_LEFT_BUTTON_DOWN |
                              ui::EF_RIGHT_BUTTON_DOWN |
                              ui::EF_MIDDLE_BUTTON_DOWN);
  int button = ui::EF_LEFT_BUTTON_DOWN;
  if (touch.identity() == 1)
    button = ui::EF_RIGHT_BUTTON_DOWN;
  else if (touch.identity() == 2)
    button = ui::EF_MIDDLE_BUTTON_DOWN;
  set_flags(new_flags | button);
}

////////////////////////////////////////////////////////////////////////////////
// MouseWheelEvent, public:

MouseWheelEvent::MouseWheelEvent(NativeEvent2 native_event_2,
                                 FromNativeEvent2 from_native)
    : MouseEvent(native_event_2, from_native),
      offset_(GetMouseWheelOffset(native_event_2)) {
}

////////////////////////////////////////////////////////////////////////////////
// TouchEvent, public:

#if defined(HAVE_XINPUT2)
TouchEvent::TouchEvent(NativeEvent2 native_event_2,
                       FromNativeEvent2 from_native)
    : LocatedEvent(native_event_2, from_native),
      touch_id_(GetTouchIDFromXEvent(native_event_2)),
      radius_(GetTouchRadiusFromXEvent(native_event_2)),
      angle_(GetTouchAngleFromXEvent(native_event_2)),
      ratio_(GetTouchRatioFromXEvent(native_event_2)) {
  if (type() == ui::ET_TOUCH_PRESSED || type() == ui::ET_TOUCH_RELEASED) {
    TouchFactory* factory = TouchFactory::GetInstance();
    float slot;
    if (factory->ExtractTouchParam(*native_event_2,
                                   TouchFactory::TP_SLOT_ID, &slot)) {
      factory->SetSlotUsed(slot, type() == ui::ET_TOUCH_PRESSED);
    }
  }
}
#endif

}  // namespace views
<commit_msg>Fix typo in GetTouchEventType<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "views/events/event.h"

#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#if defined(HAVE_XINPUT2)
#include <X11/extensions/XInput2.h>
#endif
#include <X11/Xlib.h>

#include "base/utf_string_conversions.h"
#include "ui/base/keycodes/keyboard_code_conversion_x.h"
#include "views/widget/root_view.h"
#include "views/widget/widget_gtk.h"

#if defined(HAVE_XINPUT2)
#include "views/touchui/touch_factory.h"
#endif

namespace views {

namespace {

// Scroll amount for each wheelscroll event. 53 is also the value used for GTK+.
static int kWheelScrollAmount = 53;

int GetEventFlagsFromXState(unsigned int state) {
  int flags = 0;
  if (state & ControlMask)
    flags |= ui::EF_CONTROL_DOWN;
  if (state & ShiftMask)
    flags |= ui::EF_SHIFT_DOWN;
  if (state & Mod1Mask)
    flags |= ui::EF_ALT_DOWN;
  if (state & LockMask)
    flags |= ui::EF_CAPS_LOCK_DOWN;
  if (state & Button1Mask)
    flags |= ui::EF_LEFT_BUTTON_DOWN;
  if (state & Button2Mask)
    flags |= ui::EF_MIDDLE_BUTTON_DOWN;
  if (state & Button3Mask)
    flags |= ui::EF_RIGHT_BUTTON_DOWN;

  return flags;
}

// Get the event flag for the button in XButtonEvent. During a ButtonPress
// event, |state| in XButtonEvent does not include the button that has just been
// pressed. Instead |state| contains flags for the buttons (if any) that had
// already been pressed before the current button, and |button| stores the most
// current pressed button. So, if you press down left mouse button, and while
// pressing it down, press down the right mouse button, then for the latter
// event, |state| would have Button1Mask set but not Button3Mask, and |button|
// would be 3.
int GetEventFlagsForButton(int button) {
  switch (button) {
    case 1:
      return ui::EF_LEFT_BUTTON_DOWN;
    case 2:
      return ui::EF_MIDDLE_BUTTON_DOWN;
    case 3:
      return ui::EF_RIGHT_BUTTON_DOWN;
  }

  DLOG(WARNING) << "Unexpected button (" << button << ") received.";
  return 0;
}

#if defined(HAVE_XINPUT2)
int GetButtonMaskForX2Event(XIDeviceEvent* xievent) {
  int buttonflags = 0;

  for (int i = 0; i < 8 * xievent->buttons.mask_len; i++) {
    if (XIMaskIsSet(xievent->buttons.mask, i)) {
      buttonflags |= GetEventFlagsForButton(i);
    }
  }

  return buttonflags;
}

ui::EventType GetTouchEventType(XEvent* xev) {
  XGenericEventCookie* cookie = &xev->xcookie;
  DCHECK_EQ(cookie->evtype, XI_Motion);

  // Note: We will not generate a _STATIONARY event here. It will be created,
  // when necessary, by a RWHVV.
  // TODO(sad): When should _CANCELLED be generated?

  TouchFactory* factory = TouchFactory::GetInstance();
  float slot;
  if (!factory->ExtractTouchParam(*xev, TouchFactory::TP_SLOT_ID, &slot))
    return ui::ET_UNKNOWN;

  if (!factory->IsSlotUsed(slot)) {
    // This is a new touch point.
    return ui::ET_TOUCH_PRESSED;
  }

  float tracking;
  if (!factory->ExtractTouchParam(*xev, TouchFactory::TP_TRACKING_ID,
        &tracking))
    return ui::ET_UNKNOWN;

  if (tracking == 0l) {
    // The touch point has been released.
    return ui::ET_TOUCH_RELEASED;
  }

  return ui::ET_TOUCH_MOVED;
}

int GetTouchIDFromXEvent(XEvent* xev) {
  float slot = 0;
  if (!TouchFactory::GetInstance()->ExtractTouchParam(
        *xev, TouchFactory::TP_SLOT_ID, &slot))
    LOG(ERROR) << "Could not get the slot ID for the event. Using 0.";
  return slot;
}

#endif  // HAVE_XINPUT2

ui::EventType EventTypeFromNative(NativeEvent2 native_event) {
  switch (native_event->type) {
    case KeyPress:
      return ui::ET_KEY_PRESSED;
    case KeyRelease:
      return ui::ET_KEY_RELEASED;
    case ButtonPress:
      if (native_event->xbutton.button == 4 ||
          native_event->xbutton.button == 5)
        return ui::ET_MOUSEWHEEL;
      return ui::ET_MOUSE_PRESSED;
    case ButtonRelease:
      if (native_event->xbutton.button == 4 ||
          native_event->xbutton.button == 5)
        return ui::ET_MOUSEWHEEL;
      return ui::ET_MOUSE_RELEASED;
    case MotionNotify:
      if (native_event->xmotion.state &
          (Button1Mask | Button2Mask | Button3Mask))
        return ui::ET_MOUSE_DRAGGED;
      return ui::ET_MOUSE_MOVED;
#if defined(HAVE_XINPUT2)
    case GenericEvent: {
      XIDeviceEvent* xievent =
          static_cast<XIDeviceEvent*>(native_event->xcookie.data);
      if (TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid))
        return GetTouchEventType(native_event);
      switch (xievent->evtype) {
        case XI_ButtonPress:
          return (xievent->detail == 4 || xievent->detail == 5) ?
              ui::ET_MOUSEWHEEL : ui::ET_MOUSE_PRESSED;
        case XI_ButtonRelease:
          return (xievent->detail == 4 || xievent->detail == 5) ?
              ui::ET_MOUSEWHEEL : ui::ET_MOUSE_RELEASED;
        case XI_Motion:
          return GetButtonMaskForX2Event(xievent) ? ui::ET_MOUSE_DRAGGED :
              ui::ET_MOUSE_MOVED;
      }
    }
#endif
    default:
      NOTREACHED();
      break;
  }
  return ui::ET_UNKNOWN;
}

int GetMouseWheelOffset(XEvent* xev) {
#if defined(HAVE_XINPUT2)
  if (xev->type == GenericEvent) {
    XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev->xcookie.data);
    return xiev->detail == 4 ? kWheelScrollAmount : -kWheelScrollAmount;
  }
#endif
  return xev->xbutton.button == 4 ? kWheelScrollAmount : -kWheelScrollAmount;
}

gfx::Point GetEventLocation(XEvent* xev) {
  switch (xev->type) {
    case ButtonPress:
    case ButtonRelease:
      return gfx::Point(xev->xbutton.x, xev->xbutton.y);

    case MotionNotify:
      return gfx::Point(xev->xmotion.x, xev->xmotion.y);

#if defined(HAVE_XINPUT2)
    case GenericEvent: {
      XIDeviceEvent* xievent =
          static_cast<XIDeviceEvent*>(xev->xcookie.data);
      return gfx::Point(static_cast<int>(xievent->event_x),
                        static_cast<int>(xievent->event_y));
    }
#endif
  }

  return gfx::Point();
}

int GetLocatedEventFlags(XEvent* xev) {
  switch (xev->type) {
    case ButtonPress:
    case ButtonRelease:
      return GetEventFlagsFromXState(xev->xbutton.state) |
             GetEventFlagsForButton(xev->xbutton.button);

    case MotionNotify:
      return GetEventFlagsFromXState(xev->xmotion.state);

#if defined(HAVE_XINPUT2)
    case GenericEvent: {
      XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data);
      bool touch =
          TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid);
      switch (xievent->evtype) {
        case XI_ButtonPress:
        case XI_ButtonRelease:
          return GetButtonMaskForX2Event(xievent) |
                 GetEventFlagsFromXState(xievent->mods.effective) |
                 (touch ? 0 : GetEventFlagsForButton(xievent->detail));

        case XI_Motion:
           return GetButtonMaskForX2Event(xievent) |
                  GetEventFlagsFromXState(xievent->mods.effective);
      }
    }
#endif
  }

  return 0;
}

uint16 GetCharacterFromXKeyEvent(XKeyEvent* key) {
  char buf[6];
  int bytes_written = XLookupString(key, buf, 6, NULL, NULL);
  DCHECK_LE(bytes_written, 6);

  string16 result;
  return (bytes_written > 0 && UTF8ToUTF16(buf, bytes_written, &result) &&
          result.length() == 1) ? result[0] : 0;
}

float GetTouchRadiusFromXEvent(XEvent* xev) {
  float diameter = 0.0;

#if defined(HAVE_XINPUT2)
  TouchFactory* touch_factory = TouchFactory::GetInstance();
  touch_factory->ExtractTouchParam(*xev, TouchFactory::TP_TOUCH_MAJOR,
                                   &diameter);
#endif

  return diameter / 2.0;
}

float GetTouchAngleFromXEvent(XEvent* xev) {
  float angle = 0.0;

#if defined(HAVE_XINPUT2)
  TouchFactory* touch_factory = TouchFactory::GetInstance();
  touch_factory->ExtractTouchParam(*xev, TouchFactory::TP_ORIENTATION,
                                   &angle);
#endif

  return angle;
}

float GetTouchRatioFromXEvent(XEvent* xev) {
  float ratio = 1.0;

#if defined(HAVE_XINPUT2)
  TouchFactory* touch_factory = TouchFactory::GetInstance();
  float major_v = -1.0;
  float minor_v = -1.0;

  if (!touch_factory->ExtractTouchParam(*xev,
                                        TouchFactory::TP_TOUCH_MAJOR,
                                        &major_v) ||
      !touch_factory->ExtractTouchParam(*xev,
                                        TouchFactory::TP_TOUCH_MINOR,
                                        &minor_v))
    return ratio;

  // In case minor axis exists but is zero.
  if (minor_v > 0.0)
    ratio = major_v / minor_v;
#endif

  return ratio;
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// Event, private:

void Event::InitWithNativeEvent2(NativeEvent2 native_event_2,
                                 FromNativeEvent2) {
  native_event_ = NULL;
  // TODO(beng): remove once we rid views of Gtk/Gdk.
  native_event_2_ = native_event_2;
}

////////////////////////////////////////////////////////////////////////////////
// LocatedEvent, protected:

LocatedEvent::LocatedEvent(NativeEvent2 native_event_2,
                           FromNativeEvent2 from_native)
    : Event(native_event_2,
            EventTypeFromNative(native_event_2),
            GetLocatedEventFlags(native_event_2),
            from_native),
      location_(GetEventLocation(native_event_2)) {
}

////////////////////////////////////////////////////////////////////////////////
// KeyEvent, public:

KeyEvent::KeyEvent(NativeEvent2 native_event_2, FromNativeEvent2 from_native)
    : Event(native_event_2,
            EventTypeFromNative(native_event_2),
            GetEventFlagsFromXState(native_event_2->xkey.state),
            from_native),
      key_code_(ui::KeyboardCodeFromXKeyEvent(native_event_2)) {
}

uint16 KeyEvent::GetCharacter() const {
  if (!native_event_2())
    return GetCharacterFromKeyCode(key_code_, flags());

  DCHECK(native_event_2()->type == KeyPress ||
         native_event_2()->type == KeyRelease);

  uint16 ch = GetCharacterFromXKeyEvent(&native_event_2()->xkey);
  return ch ? ch : GetCharacterFromKeyCode(key_code_, flags());
}

uint16 KeyEvent::GetUnmodifiedCharacter() const {
  if (!native_event_2())
    return GetCharacterFromKeyCode(key_code_, flags() & ui::EF_SHIFT_DOWN);

  DCHECK(native_event_2()->type == KeyPress ||
         native_event_2()->type == KeyRelease);

  XKeyEvent key = native_event_2()->xkey;

  static const unsigned int kIgnoredModifiers = ControlMask | LockMask |
      Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask;

  // We can't use things like (key.state & ShiftMask), as it may mask out bits
  // used by X11 internally.
  key.state &= ~kIgnoredModifiers;
  uint16 ch = GetCharacterFromXKeyEvent(&key);
  return ch ? ch :
      GetCharacterFromKeyCode(key_code_, flags() & ui::EF_SHIFT_DOWN);
}

////////////////////////////////////////////////////////////////////////////////
// MouseEvent, public:

MouseEvent::MouseEvent(NativeEvent2 native_event_2,
                       FromNativeEvent2 from_native)
    : LocatedEvent(native_event_2, from_native) {
}

MouseEvent::MouseEvent(const TouchEvent& touch,
                       FromNativeEvent2 from_native)
    : LocatedEvent(touch.native_event_2(), from_native) {
  // The location of the event is correctly extracted from the native event. But
  // it is necessary to update the event type.
  ui::EventType mtype = ui::ET_UNKNOWN;
  switch (touch.type()) {
    case ui::ET_TOUCH_RELEASED:
      mtype = ui::ET_MOUSE_RELEASED;
      break;
    case ui::ET_TOUCH_PRESSED:
      mtype = ui::ET_MOUSE_PRESSED;
      break;
    case ui::ET_TOUCH_MOVED:
      mtype = ui::ET_MOUSE_MOVED;
      break;
    default:
      NOTREACHED() << "Invalid mouse event.";
  }
  set_type(mtype);

  // It may not be possible to extract the button-information necessary for a
  // MouseEvent from the native event for a TouchEvent, so the flags are
  // explicitly updated as well. The button is approximated from the touchpoint
  // identity.
  int new_flags = flags() & ~(ui::EF_LEFT_BUTTON_DOWN |
                              ui::EF_RIGHT_BUTTON_DOWN |
                              ui::EF_MIDDLE_BUTTON_DOWN);
  int button = ui::EF_LEFT_BUTTON_DOWN;
  if (touch.identity() == 1)
    button = ui::EF_RIGHT_BUTTON_DOWN;
  else if (touch.identity() == 2)
    button = ui::EF_MIDDLE_BUTTON_DOWN;
  set_flags(new_flags | button);
}

////////////////////////////////////////////////////////////////////////////////
// MouseWheelEvent, public:

MouseWheelEvent::MouseWheelEvent(NativeEvent2 native_event_2,
                                 FromNativeEvent2 from_native)
    : MouseEvent(native_event_2, from_native),
      offset_(GetMouseWheelOffset(native_event_2)) {
}

////////////////////////////////////////////////////////////////////////////////
// TouchEvent, public:

#if defined(HAVE_XINPUT2)
TouchEvent::TouchEvent(NativeEvent2 native_event_2,
                       FromNativeEvent2 from_native)
    : LocatedEvent(native_event_2, from_native),
      touch_id_(GetTouchIDFromXEvent(native_event_2)),
      radius_(GetTouchRadiusFromXEvent(native_event_2)),
      angle_(GetTouchAngleFromXEvent(native_event_2)),
      ratio_(GetTouchRatioFromXEvent(native_event_2)) {
  if (type() == ui::ET_TOUCH_PRESSED || type() == ui::ET_TOUCH_RELEASED) {
    TouchFactory* factory = TouchFactory::GetInstance();
    float slot;
    if (factory->ExtractTouchParam(*native_event_2,
                                   TouchFactory::TP_SLOT_ID, &slot)) {
      factory->SetSlotUsed(slot, type() == ui::ET_TOUCH_PRESSED);
    }
  }
}
#endif

}  // namespace views
<|endoftext|>


<commit_before>//=====================================================================//
/*! @file
    @brief  RX65N デジタル・ストレージ・オシロスコープ @n
			マイコン内臓１２ビットＡ／Ｄ変換を使って、波形を観測するガジェット
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include "common/renesas.hpp"
#include "common/cmt_io.hpp"
#include "common/fixed_fifo.hpp"
#include "common/sci_io.hpp"
#include "common/sci_i2c_io.hpp"
#include "common/format.hpp"
#include "common/command.hpp"
#include "common/spi_io2.hpp"
#include "common/sdc_man.hpp"
#include "common/tpu_io.hpp"
#include "common/qspi_io.hpp"
#include "graphics/font8x16.hpp"
#include "graphics/graphics.hpp"
#include "graphics/filer.hpp"

#define CASH_KFONT
#include "graphics/kfont.hpp"

#include "chip/FT5206.hpp"

#include "capture.hpp"
#include "render_wave.hpp"

namespace {

	typedef device::PORT<device::PORT7, device::bitpos::B0> LED;

	typedef device::system_io<12000000> SYSTEM_IO;

	device::cmt_io<device::CMT0, utils::null_task>  cmt_;

	typedef utils::fixed_fifo<char, 512>  RECV_BUFF;
	typedef utils::fixed_fifo<char, 1024> SEND_BUFF;
	typedef device::sci_io<device::SCI9, RECV_BUFF, SEND_BUFF> SCI;
	SCI		sci_;

	// カード電源制御は使わないので、「device::NULL_PORT」を指定する。
//	typedef device::PORT<device::PORT6, device::bitpos::B4> SDC_POWER;
	typedef device::NULL_PORT SDC_POWER;

#ifdef SDHI_IF
	// RX65N Envision Kit の SDHI ポートは、候補３になっている
	typedef fatfs::sdhi_io<device::SDHI, SDC_POWER, device::port_map::option::THIRD> SDHI;
	SDHI	sdh_;
#else
	// Soft SDC 用　SPI 定義（SPI）
	typedef device::PORT<device::PORT2, device::bitpos::B2> MISO;  // DAT0
	typedef device::PORT<device::PORT2, device::bitpos::B0> MOSI;  // CMD
	typedef device::PORT<device::PORT2, device::bitpos::B1> SPCK;  // CLK

	typedef device::spi_io2<MISO, MOSI, SPCK> SPI;  ///< Soft SPI 定義

	SPI		spi_;

	typedef device::PORT<device::PORT1, device::bitpos::B7> SDC_SELECT;  // DAT3 カード選択信号
	typedef device::PORT<device::PORT2, device::bitpos::B5> SDC_DETECT;  // CD   カード検出

	typedef fatfs::mmc_io<SPI, SDC_SELECT, SDC_POWER, SDC_DETECT> MMC;   // ハードウェアー定義

	MMC		sdh_(spi_, 20000000);
#endif
	typedef utils::sdc_man SDC;
	SDC		sdc_;

	typedef device::PORT<device::PORT6, device::bitpos::B3> LCD_DISP;
	typedef device::PORT<device::PORT6, device::bitpos::B6> LCD_LIGHT;
	typedef device::glcdc_io<device::GLCDC, 480, 272,
		device::glcdc_def::PIX_TYPE::RGB565> GLCDC_IO;
///		device::glcdc_def::PIX_TYPE::CLUT8> GLCDC_IO;
	GLCDC_IO	glcdc_io_;

	typedef device::drw2d_mgr<device::DRW2D, 480, 272> DRW2D_MGR;
	DRW2D_MGR	drw2d_mgr_;

	typedef graphics::font8x16 AFONT;
#ifdef CASH_KFONT
	typedef graphics::kfont<16, 16, 64> KFONT;
#else
	typedef graphics::kfont<16, 16> KFONT;
#endif
	KFONT		kfont_;

	typedef graphics::render<uint16_t, 480, 272, AFONT, KFONT> RENDER;
	RENDER		render_(reinterpret_cast<uint16_t*>(0x00000000), kfont_);

	typedef utils::capture<2048> CAPTURE;
	CAPTURE		capture_;

	// FT5206, SCI6 簡易 I2C 定義
	typedef device::PORT<device::PORT0, device::bitpos::B7> FT5206_RESET;
	typedef utils::fixed_fifo<uint8_t, 64> RB6;
	typedef utils::fixed_fifo<uint8_t, 64> SB6;
	typedef device::sci_i2c_io<device::SCI6, RB6, SB6,
		device::port_map::option::FIRST_I2C> FT5206_I2C;

	FT5206_I2C	ft5206_i2c_;
	typedef chip::FT5206<FT5206_I2C> FT5206;
	FT5206		ft5206_(ft5206_i2c_);

	typedef utils::render_wave<RENDER, CAPTURE, FT5206> RENDER_WAVE;
	RENDER_WAVE	render_wave_(render_, capture_, ft5206_);

	utils::command<256> cmd_;

	utils::capture_trigger	trigger_ = utils::capture_trigger::NONE;

	bool check_mount_() {
		return sdc_.get_mount();
	}

	void update_led_()
	{
		static uint8_t n = 0;
		++n;
		if(n >= 30) {
			n = 0;
		}
		if(n < 10) {
			LED::P = 0;
		} else {
			LED::P = 1;
		}
	}

	void command_()
	{
		if(!cmd_.service()) {
			return;
		}

		uint8_t cmdn = cmd_.get_words();
		if(cmdn >= 1) {
			bool f = false;
			if(cmd_.cmp_word(0, "dir")) {  // dir [xxx]
				if(check_mount_()) {
					if(cmdn >= 2) {
						char tmp[128];
						cmd_.get_word(1, sizeof(tmp), tmp);
						sdc_.dir(tmp);
					} else {
						sdc_.dir("");
					}
				}
				f = true;
			} else if(cmd_.cmp_word(0, "cd")) {  // cd [xxx]
				if(check_mount_()) {
					if(cmdn >= 2) {
						char tmp[128];
						cmd_.get_word(1, sizeof(tmp), tmp);
						sdc_.cd(tmp);
					} else {
						sdc_.cd("/");
					}
				}
				f = true;
			} else if(cmd_.cmp_word(0, "pwd")) { // pwd
				utils::format("%s\n") % sdc_.get_current();
				f = true;
			} else if(cmd_.cmp_word(0, "cap")) { // capture
				trigger_ = utils::capture_trigger::SINGLE;
				capture_.set_trigger(trigger_);
				f = true;
			} else if(cmd_.cmp_word(0, "help")) {
				utils::format("    dir [path]\n");
				utils::format("    cd [path]\n");
				utils::format("    pwd\n");
				f = true;
			}
			if(!f) {
				char tmp[128];
				if(cmd_.get_word(0, sizeof(tmp), tmp)) {
					utils::format("Command error: '%s'\n") % tmp;
				}
			}
		}
	}
}

extern "C" {

	void sci_putch(char ch)
	{
		sci_.putch(ch);
	}

	void sci_puts(const char* str)
	{
		sci_.puts(str);
	}

	char sci_getch(void)
	{
		return sci_.getch();
	}

	uint16_t sci_length()
	{
		return sci_.recv_length();
	}

	DSTATUS disk_initialize(BYTE drv) {
		return sdh_.disk_initialize(drv);
	}

	DSTATUS disk_status(BYTE drv) {
		return sdh_.disk_status(drv);
	}

	DRESULT disk_read(BYTE drv, BYTE* buff, DWORD sector, UINT count) {
		return sdh_.disk_read(drv, buff, sector, count);
	}

	DRESULT disk_write(BYTE drv, const BYTE* buff, DWORD sector, UINT count) {
		return sdh_.disk_write(drv, buff, sector, count);
	}

	DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void* buff) {
		return sdh_.disk_ioctl(drv, ctrl, buff);
	}

	DWORD get_fattime(void) {
		time_t t = 0;
///		rtc_.get_time(t);
		return utils::str::get_fattime(t);
	}

	// for syscalls API (POSIX API)
	void utf8_to_sjis(const char* src, char* dst, uint32_t len) {
		utils::str::utf8_to_sjis(src, dst, len);
	}

	int fatfs_get_mount() {
		return check_mount_();
	}

	int make_full_path(const char* src, char* dst, uint16_t len)
	{
		return sdc_.make_full_path(src, dst, len);
	}
}

int main(int argc, char** argv);

int main(int argc, char** argv)
{
	SYSTEM_IO::setup_system_clock();

	{  // SCI 設定
		static const uint8_t sci_level = 2;
		sci_.start(115200, sci_level);
	}

	{  // SD カード・クラスの初期化
		sdh_.start();
		sdc_.start();
	}

	{  // キャプチャー開始
//		uint32_t freq = 2000000;  // 2 MHz
		uint32_t freq = 100000;  // 100 KHz
		if(!capture_.start(freq)) {
			utils::format("Capture not start...\n");
		}
	}

	utils::format("RTK5RX65N Start for Digital Storage Oscilloscope\n");
	cmd_.set_prompt("# ");

	{  // GLCDC の初期化
		LCD_DISP::DIR  = 1;
		LCD_LIGHT::DIR = 1;
		LCD_DISP::P  = 0;  // DISP Disable
		LCD_LIGHT::P = 0;  // BackLight Disable (No PWM)
		if(glcdc_io_.start()) {
			utils::format("Start GLCDC\n");
			LCD_DISP::P  = 1;  // DISP Enable
			LCD_LIGHT::P = 1;  // BackLight Enable (No PWM)
			if(!glcdc_io_.control(GLCDC_IO::CONTROL_CMD::START_DISPLAY)) {
				utils::format("GLCDC ctrl fail...\n");
			}
		} else {
			utils::format("GLCDC Fail\n");
		}
	}

	{  // DRW2D 初期化
		auto ver = drw2d_mgr_.get_version();
		utils::format("DRW2D Version: %04X\n") % ver;

		if(drw2d_mgr_.start(0x00000000)) {
			utils:: format("Start DRW2D\n");
		} else {
			utils:: format("DRW2D Fail\n");
		}
	}

	{  // FT5206 touch screen controller
		FT5206::reset<FT5206_RESET>();
		uint8_t intr_lvl = 1;
		if(!ft5206_i2c_.start(FT5206_I2C::SPEED::STANDARD, intr_lvl)) {
			utils::format("FT5206 I2C Start Fail...\n");
		}
		if(!ft5206_.start()) {
			utils::format("FT5206 Start Fail...\n");
		}
	}

	LED::DIR = 1;

	{  // startup trigger...
		trigger_ = utils::capture_trigger::SINGLE;
		capture_.set_trigger(trigger_);
	}

	// タッチパネルの安定待ち
	{
		uint8_t nnn = 0;
		while(1) {
			glcdc_io_.sync_vpos();
			ft5206_.update();
			if(ft5206_.get_touch_num() == 0) {
				++nnn;
				if(nnn >= 60) break;
			} else {
				nnn = 0;
			}
		}
	}

	while(1) {
		glcdc_io_.sync_vpos();

		ft5206_.update();

		sdc_.service(sdh_.service());

		command_();

		// タッチ操作による画面更新が必要か？
		bool f = render_wave_.ui_service();

		// 波形をキャプチャーしたら描画
		if(f || (trigger_ != utils::capture_trigger::NONE
			&& capture_.get_trigger() == utils::capture_trigger::NONE)) {
			trigger_ = utils::capture_trigger::NONE;
			render_wave_.update();
		}

		update_led_();
	}
}
<commit_msg>update: touch device startup<commit_after>//=====================================================================//
/*! @file
    @brief  RX65N デジタル・ストレージ・オシロスコープ @n
			マイコン内臓１２ビットＡ／Ｄ変換を使って、波形を観測するガジェット
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include "common/renesas.hpp"
#include "common/cmt_io.hpp"
#include "common/fixed_fifo.hpp"
#include "common/sci_io.hpp"
#include "common/sci_i2c_io.hpp"
#include "common/format.hpp"
#include "common/command.hpp"
#include "common/spi_io2.hpp"
#include "common/sdc_man.hpp"
#include "common/tpu_io.hpp"
#include "common/qspi_io.hpp"
#include "graphics/font8x16.hpp"
#include "graphics/graphics.hpp"
#include "graphics/filer.hpp"

#define CASH_KFONT
#include "graphics/kfont.hpp"

#include "chip/FT5206.hpp"

#include "capture.hpp"
#include "render_wave.hpp"

namespace {

	typedef device::PORT<device::PORT7, device::bitpos::B0> LED;

	typedef device::system_io<12000000> SYSTEM_IO;

	device::cmt_io<device::CMT0, utils::null_task>  cmt_;

	typedef utils::fixed_fifo<char, 512>  RECV_BUFF;
	typedef utils::fixed_fifo<char, 1024> SEND_BUFF;
	typedef device::sci_io<device::SCI9, RECV_BUFF, SEND_BUFF> SCI;
	SCI		sci_;

	// カード電源制御は使わないので、「device::NULL_PORT」を指定する。
//	typedef device::PORT<device::PORT6, device::bitpos::B4> SDC_POWER;
	typedef device::NULL_PORT SDC_POWER;

#ifdef SDHI_IF
	// RX65N Envision Kit の SDHI ポートは、候補３になっている
	typedef fatfs::sdhi_io<device::SDHI, SDC_POWER, device::port_map::option::THIRD> SDHI;
	SDHI	sdh_;
#else
	// Soft SDC 用　SPI 定義（SPI）
	typedef device::PORT<device::PORT2, device::bitpos::B2> MISO;  // DAT0
	typedef device::PORT<device::PORT2, device::bitpos::B0> MOSI;  // CMD
	typedef device::PORT<device::PORT2, device::bitpos::B1> SPCK;  // CLK

	typedef device::spi_io2<MISO, MOSI, SPCK> SPI;  ///< Soft SPI 定義

	SPI		spi_;

	typedef device::PORT<device::PORT1, device::bitpos::B7> SDC_SELECT;  // DAT3 カード選択信号
	typedef device::PORT<device::PORT2, device::bitpos::B5> SDC_DETECT;  // CD   カード検出

	typedef fatfs::mmc_io<SPI, SDC_SELECT, SDC_POWER, SDC_DETECT> MMC;   // ハードウェアー定義

	MMC		sdh_(spi_, 20000000);
#endif
	typedef utils::sdc_man SDC;
	SDC		sdc_;

	typedef device::PORT<device::PORT6, device::bitpos::B3> LCD_DISP;
	typedef device::PORT<device::PORT6, device::bitpos::B6> LCD_LIGHT;
	typedef device::glcdc_io<device::GLCDC, 480, 272,
		device::glcdc_def::PIX_TYPE::RGB565> GLCDC_IO;
///		device::glcdc_def::PIX_TYPE::CLUT8> GLCDC_IO;
	GLCDC_IO	glcdc_io_;

	typedef device::drw2d_mgr<device::DRW2D, 480, 272> DRW2D_MGR;
	DRW2D_MGR	drw2d_mgr_;

	typedef graphics::font8x16 AFONT;
#ifdef CASH_KFONT
	typedef graphics::kfont<16, 16, 64> KFONT;
#else
	typedef graphics::kfont<16, 16> KFONT;
#endif
	KFONT		kfont_;

	typedef graphics::render<uint16_t, 480, 272, AFONT, KFONT> RENDER;
	RENDER		render_(reinterpret_cast<uint16_t*>(0x00000000), kfont_);

	typedef utils::capture<2048> CAPTURE;
	CAPTURE		capture_;

	// FT5206, SCI6 簡易 I2C 定義
	typedef device::PORT<device::PORT0, device::bitpos::B7> FT5206_RESET;
	typedef utils::fixed_fifo<uint8_t, 64> RB6;
	typedef utils::fixed_fifo<uint8_t, 64> SB6;
	typedef device::sci_i2c_io<device::SCI6, RB6, SB6,
		device::port_map::option::FIRST_I2C> FT5206_I2C;

	FT5206_I2C	ft5206_i2c_;
	typedef chip::FT5206<FT5206_I2C> FT5206;
	FT5206		ft5206_(ft5206_i2c_);

	typedef utils::render_wave<RENDER, CAPTURE, FT5206> RENDER_WAVE;
	RENDER_WAVE	render_wave_(render_, capture_, ft5206_);

	utils::command<256> cmd_;

	utils::capture_trigger	trigger_ = utils::capture_trigger::NONE;

	bool check_mount_() {
		return sdc_.get_mount();
	}

	void update_led_()
	{
		static uint8_t n = 0;
		++n;
		if(n >= 30) {
			n = 0;
		}
		if(n < 10) {
			LED::P = 0;
		} else {
			LED::P = 1;
		}
	}

	void command_()
	{
		if(!cmd_.service()) {
			return;
		}

		uint8_t cmdn = cmd_.get_words();
		if(cmdn >= 1) {
			bool f = false;
			if(cmd_.cmp_word(0, "dir")) {  // dir [xxx]
				if(check_mount_()) {
					if(cmdn >= 2) {
						char tmp[128];
						cmd_.get_word(1, sizeof(tmp), tmp);
						sdc_.dir(tmp);
					} else {
						sdc_.dir("");
					}
				}
				f = true;
			} else if(cmd_.cmp_word(0, "cd")) {  // cd [xxx]
				if(check_mount_()) {
					if(cmdn >= 2) {
						char tmp[128];
						cmd_.get_word(1, sizeof(tmp), tmp);
						sdc_.cd(tmp);
					} else {
						sdc_.cd("/");
					}
				}
				f = true;
			} else if(cmd_.cmp_word(0, "pwd")) { // pwd
				utils::format("%s\n") % sdc_.get_current();
				f = true;
			} else if(cmd_.cmp_word(0, "cap")) { // capture
				trigger_ = utils::capture_trigger::SINGLE;
				capture_.set_trigger(trigger_);
				f = true;
			} else if(cmd_.cmp_word(0, "help")) {
				utils::format("    dir [path]\n");
				utils::format("    cd [path]\n");
				utils::format("    pwd\n");
				utils::format("    cap   single trigger\n");
				f = true;
			}
			if(!f) {
				char tmp[128];
				if(cmd_.get_word(0, sizeof(tmp), tmp)) {
					utils::format("Command error: '%s'\n") % tmp;
				}
			}
		}
	}
}

extern "C" {

	void sci_putch(char ch)
	{
		sci_.putch(ch);
	}

	void sci_puts(const char* str)
	{
		sci_.puts(str);
	}

	char sci_getch(void)
	{
		return sci_.getch();
	}

	uint16_t sci_length()
	{
		return sci_.recv_length();
	}

	DSTATUS disk_initialize(BYTE drv) {
		return sdh_.disk_initialize(drv);
	}

	DSTATUS disk_status(BYTE drv) {
		return sdh_.disk_status(drv);
	}

	DRESULT disk_read(BYTE drv, BYTE* buff, DWORD sector, UINT count) {
		return sdh_.disk_read(drv, buff, sector, count);
	}

	DRESULT disk_write(BYTE drv, const BYTE* buff, DWORD sector, UINT count) {
		return sdh_.disk_write(drv, buff, sector, count);
	}

	DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void* buff) {
		return sdh_.disk_ioctl(drv, ctrl, buff);
	}

	DWORD get_fattime(void) {
		time_t t = 0;
///		rtc_.get_time(t);
		return utils::str::get_fattime(t);
	}

	// for syscalls API (POSIX API)
	void utf8_to_sjis(const char* src, char* dst, uint32_t len) {
		utils::str::utf8_to_sjis(src, dst, len);
	}

	int fatfs_get_mount() {
		return check_mount_();
	}

	int make_full_path(const char* src, char* dst, uint16_t len)
	{
		return sdc_.make_full_path(src, dst, len);
	}
}

int main(int argc, char** argv);

int main(int argc, char** argv)
{
	SYSTEM_IO::setup_system_clock();

	{  // SCI 設定
		static const uint8_t sci_level = 2;
		sci_.start(115200, sci_level);
	}

	{  // SD カード・クラスの初期化
		sdh_.start();
		sdc_.start();
	}

	{  // キャプチャー開始
//		uint32_t freq = 2000000;  // 2 MHz
		uint32_t freq = 100000;  // 100 KHz
		if(!capture_.start(freq)) {
			utils::format("Capture not start...\n");
		}
	}

	utils::format("RTK5RX65N Start for Digital Storage Oscilloscope\n");
	cmd_.set_prompt("# ");

	{  // GLCDC の初期化
		LCD_DISP::DIR  = 1;
		LCD_LIGHT::DIR = 1;
		LCD_DISP::P  = 0;  // DISP Disable
		LCD_LIGHT::P = 0;  // BackLight Disable (No PWM)
		if(glcdc_io_.start()) {
			utils::format("Start GLCDC\n");
			LCD_DISP::P  = 1;  // DISP Enable
			LCD_LIGHT::P = 1;  // BackLight Enable (No PWM)
			if(!glcdc_io_.control(GLCDC_IO::CONTROL_CMD::START_DISPLAY)) {
				utils::format("GLCDC ctrl fail...\n");
			}
		} else {
			utils::format("GLCDC Fail\n");
		}
	}

	{  // DRW2D 初期化
		auto ver = drw2d_mgr_.get_version();
		utils::format("DRW2D Version: %04X\n") % ver;

		if(drw2d_mgr_.start(0x00000000)) {
			utils:: format("Start DRW2D\n");
		} else {
			utils:: format("DRW2D Fail\n");
		}
	}

	{  // FT5206 touch screen controller
		FT5206::reset<FT5206_RESET>();
		uint8_t intr_lvl = 1;
		if(!ft5206_i2c_.start(FT5206_I2C::SPEED::STANDARD, intr_lvl)) {
			utils::format("FT5206 I2C Start Fail...\n");
		}
		if(!ft5206_.start()) {
			utils::format("FT5206 Start Fail...\n");
		}
	}

	LED::DIR = 1;

	{  // startup trigger...
		trigger_ = utils::capture_trigger::SINGLE;
		capture_.set_trigger(trigger_);
	}

	// タッチパネルの安定待ち
#if 0
	{
		uint8_t nnn = 0;
		while(1) {
			glcdc_io_.sync_vpos();
			ft5206_.update();
//			if(ft5206_.get_touch_num() == 0) {
//				++nnn;
///				if(nnn >= 60) break;
//			} else {
// utils::format("%d\n") % static_cast<uint16_t>(ft5206_.get_touch_num());
//				nnn = 0;
//			}
		}
	}
#endif

	while(1) {
		glcdc_io_.sync_vpos();

		ft5206_.update();

		sdc_.service(sdh_.service());

		command_();

		// タッチ操作による画面更新が必要か？
		bool f = render_wave_.ui_service();

		// 波形をキャプチャーしたら描画
		if(f || (trigger_ != utils::capture_trigger::NONE
			&& capture_.get_trigger() == utils::capture_trigger::NONE)) {
			trigger_ = utils::capture_trigger::NONE;
			render_wave_.update();
		}

		update_led_();
	}
}
<|endoftext|>


<commit_before>//=====================================================================//
/*! @file
    @brief  RX65N SIDE
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include "common/renesas.hpp"
#include "common/fixed_fifo.hpp"
#include "common/sci_io.hpp"
#include "common/format.hpp"
#include "common/command.hpp"
#include "common/spi_io2.hpp"
#include "common/sdc_man.hpp"
#include "common/tpu_io.hpp"
#include "common/audio_out.hpp"

#include "chip/FAMIPAD.hpp"

#include "spinv.hpp"

namespace {

	typedef device::PORT<device::PORT7, device::bitpos::B0> LED;
// オーディオの D/A として使用
//	typedef device::PORT<device::PORT0, device::bitpos::B5> SW2;

	// Famicon PAD (CMOS 4021B Shift Register)
	// 電源は、微小なので、接続を簡単に行う為、ポートを使う
	typedef device::PORT<device::PORT6, device::bitpos::B0> PAD_VCC;
	typedef device::PORT<device::PORT6, device::bitpos::B1> PAD_GND;
	typedef device::PORT<device::PORT6, device::bitpos::B2> PAD_P_S;
	typedef device::PORT<device::PORT6, device::bitpos::B5> PAD_CLK;
	typedef device::PORT<device::PORT7, device::bitpos::B3> PAD_OUT;
	typedef chip::FAMIPAD<PAD_P_S, PAD_CLK, PAD_OUT> FAMIPAD;
	FAMIPAD		famipad_;

	typedef device::system_io<12000000> SYSTEM_IO;

	typedef utils::fixed_fifo<char, 512>  RECV_BUFF;
	typedef utils::fixed_fifo<char, 1024> SEND_BUFF;
	typedef device::sci_io<device::SCI9, RECV_BUFF, SEND_BUFF> SCI;
	SCI		sci_;

	typedef device::PORT<device::PORT6, device::bitpos::B3> LCD_DISP;
	typedef device::PORT<device::PORT6, device::bitpos::B6> LCD_LIGHT;
	typedef device::glcdc_io<device::GLCDC, 480, 272, device::PIX_TYPE::RGB565> GLCDC_IO;
	GLCDC_IO	glcdc_io_;

	// カード電源制御は使わない場合、「device::NULL_PORT」を指定する。
//	typedef device::PORT<device::PORT6, device::bitpos::B4> SDC_POWER;
	typedef device::NULL_PORT SDC_POWER;

#ifdef SDHI_IF
	typedef fatfs::sdhi_io<device::SDHI, SDC_POWER> SDHI;
	SDHI	sdh_;
#else
	// Soft SDC 用　SPI 定義（SPI）
	typedef device::PORT<device::PORT2, device::bitpos::B2> MISO;  // DAT0
	typedef device::PORT<device::PORT2, device::bitpos::B0> MOSI;  // CMD
	typedef device::PORT<device::PORT2, device::bitpos::B1> SPCK;  // CLK

	typedef device::spi_io2<MISO, MOSI, SPCK> SPI;  ///< Soft SPI 定義

	SPI		spi_;

	typedef device::PORT<device::PORT1, device::bitpos::B7> SDC_SELECT;  // DAT3 カード選択信号
	typedef device::PORT<device::PORT2, device::bitpos::B5> SDC_DETECT;  // CD   カード検出

	typedef fatfs::mmc_io<SPI, SDC_SELECT, SDC_POWER, SDC_DETECT> MMC;   // ハードウェアー定義

	MMC		sdh_(spi_, 20000000);
#endif
	typedef utils::sdc_man SDC;
	SDC		sdc_;

	volatile uint32_t	wpos_;

	/// DMAC 終了割り込み
	class dmac_term_task {
	public:
		void operator() () {
			device::DMAC0::DMCNT.DTE = 1;  // DMA を再スタート
			wpos_ = 0;
		}
	};

	typedef device::dmac_mgr<device::DMAC0, dmac_term_task> DMAC_MGR;
	DMAC_MGR	dmac_mgr_;

	uint32_t get_wave_pos_() { return (dmac_mgr_.get_count() & 0x3ff) ^ 0x3ff; }

	typedef device::R12DA DAC;
	typedef device::dac_out<DAC> DAC_OUT;
	DAC_OUT		dac_out_;

	typedef utils::audio_out<1024, 256> AUDIO_OUT;
	AUDIO_OUT	audio_out_;

	class tpu_task {
	public:
		void operator() () {
			uint32_t tmp = wpos_;
			++wpos_;
			if((tmp ^ wpos_) & 64) {
				audio_out_.service(64);
			}
		}
	};

	typedef device::tpu_io<device::TPU0, tpu_task> TPU0;
	TPU0		tpu0_;

	typedef emu::spinv SPINV;
	SPINV		spinv_;

	utils::command<256> cmd_;

	bool check_mount_() {
		auto f = sdc_.get_mount();
		if(!f) {
			utils::format("SD card not mount.\n");
		}
		return f;
	}

	void command_()
	{
		if(!cmd_.service()) {
			return;
		}

		uint8_t cmdn = cmd_.get_words();
		if(cmdn >= 1) {
			bool f = false;
			if(cmd_.cmp_word(0, "dir")) {  // dir [xxx]
				if(check_mount_()) {
					if(cmdn >= 2) {
						char tmp[128];
						cmd_.get_word(1, sizeof(tmp), tmp);
						sdc_.dir(tmp);
					} else {
						sdc_.dir("");
					}
				}
				f = true;
			} else if(cmd_.cmp_word(0, "cd")) {  // cd [xxx]
				if(check_mount_()) {
					if(cmdn >= 2) {
						char tmp[128];
						cmd_.get_word(1, sizeof(tmp), tmp);
						sdc_.cd(tmp);
					} else {
						sdc_.cd("/");
					}
				}
				f = true;
			} else if(cmd_.cmp_word(0, "pwd")) { // pwd
				utils::format("%s\n") % sdc_.get_current();
				f = true;
			} else if(cmd_.cmp_word(0, "help")) {
				utils::format("    dir [path]\n");
				utils::format("    cd [path]\n");
				utils::format("    pwd\n");
				f = true;
			}
			if(!f) {
				char tmp[128];
				if(cmd_.get_word(0, sizeof(tmp), tmp)) {
					utils::format("Command error: '%s'\n") % tmp;
				}
			}
		}
	}
}

extern "C" {

	uint8_t get_fami_pad()
	{
		return famipad_.update();
	}

	void set_sample_rate(uint32_t freq)
	{
		uint8_t intr_level = 5;
		if(!tpu0_.start(freq, intr_level)) {
			utils::format("TPU0 start error...\n");
		}
	}

	void sci_putch(char ch)
	{
		sci_.putch(ch);
	}

	void sci_puts(const char* str)
	{
		sci_.puts(str);
	}

	char sci_getch(void)
	{
		return sci_.getch();
	}

	uint16_t sci_length()
	{
		return sci_.recv_length();
	}

	DSTATUS disk_initialize(BYTE drv) {
		return sdh_.disk_initialize(drv);
	}

	DSTATUS disk_status(BYTE drv) {
		return sdh_.disk_status(drv);
	}

	DRESULT disk_read(BYTE drv, BYTE* buff, DWORD sector, UINT count) {
		return sdh_.disk_read(drv, buff, sector, count);
	}

	DRESULT disk_write(BYTE drv, const BYTE* buff, DWORD sector, UINT count) {
		return sdh_.disk_write(drv, buff, sector, count);
	}

	DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void* buff) {
		return sdh_.disk_ioctl(drv, ctrl, buff);
	}

	DWORD get_fattime(void) {
		time_t t = 0;
///		rtc_.get_time(t);
		return utils::str::get_fattime(t);
	}

	void utf8_to_sjis(const char* src, char* dst, uint32_t len) {
		utils::str::utf8_to_sjis(src, dst, len);
	}

	int fatfs_get_mount() {
		return sdc_.get_mount();
	}
}

int main(int argc, char** argv);

int main(int argc, char** argv)
{
	SYSTEM_IO::setup_system_clock();

	{  // 内臓１２ビット D/A の設定
		bool amp_ena = true;
		dac_out_.start(DAC_OUT::output::CH0_CH1, amp_ena);
		dac_out_.out0(0x8000);
		dac_out_.out1(0x8000);
	}

	{  // SCI 設定
		static const uint8_t sci_level = 2;
		sci_.start(115200, sci_level);
	}

	{  // SD カード・クラスの初期化
		sdh_.start();
		sdc_.start();
	}

	// 波形メモリーの無音状態初期化
	audio_out_.mute();

	{  // サンプリング・タイマー設定
		set_sample_rate(11127);
	}

	{  // DMAC マネージャー開始
		uint8_t intr_level = 4;
		auto ret = dmac_mgr_.start(tpu0_.get_intr_vec(), DMAC_MGR::trans_type::SP_DN_32,
			reinterpret_cast<uint32_t>(audio_out_.get_wave()), DAC::DADR0.address(),
			audio_out_.size(), intr_level);
		if(!ret) {
			utils::format("DMAC Not start...\n");
		}
	}

	utils::format("\rRTK5RX65N Start for Space Invaders\n");

	cmd_.set_prompt("# ");

	{  // GLCDC 初期化
		LCD_DISP::DIR  = 1;
		LCD_LIGHT::DIR = 1;
		LCD_DISP::P  = 0;  // DISP Disable
		LCD_LIGHT::P = 0;  // BackLight Disable (No PWM)
		if(glcdc_io_.start()) {
			utils::format("Start GLCDC\n");
			LCD_DISP::P  = 1;  // DISP Enable
			LCD_LIGHT::P = 1;  // BackLight Enable (No PWM)
			if(!glcdc_io_.control(GLCDC_IO::control_cmd::START_DISPLAY)) {
				utils::format("GLCDC ctrl fail...\n");
			}
		} else {
			utils::format("Fail GLCDC\n");
		}
	}

	LED::DIR = 1;

	{
		PAD_VCC::DIR = 1;
		PAD_VCC::P = 1;
		PAD_GND::DIR = 1;
		PAD_GND::P = 0;
		famipad_.start();
	}

	uint32_t delay_inv = 120;
	uint8_t n = 0;
	while(1) {
		glcdc_io_.sync_vpos();

		if(delay_inv > 0) {
			--delay_inv;
			if(delay_inv == 0) {
				if(sdc_.get_mount()) {
					if(spinv_.start("/inv_roms", "/inv_wavs")) {
						utils::format("Space Invaders start...\n");
					} else {
						utils::format("Space Invaders not start...(fail)\n");
					}
				} else {
					delay_inv = 60;
					utils::format("SD card not mount\n");
				}
			}
		} else {
			spinv_.service((void*)0x00000000, glcdc_io_.get_xsize(), glcdc_io_.get_ysize());

			SPINV::SND_MGR& snd = spinv_.at_sound();
			uint32_t len = snd.get_length();
			const int16_t* wav = snd.get_buffer();
			for(uint32_t i = 0; i < len; ++i) {
				while((audio_out_.at_fifo().size() - audio_out_.at_fifo().length()) < 8) {
				}
				audio::wave_t t;
				t.l_ch = t.r_ch = *wav++;
				audio_out_.at_fifo().put(t);
			}
		}

		sdc_.service(sdh_.service());

		command_();

		++n;
		if(n >= 30) {
			n = 0;
		}
		if(n < 10) {
			LED::P = 0;
		} else {
			LED::P = 1;
		}
	}
}
<commit_msg>update: add full_path API<commit_after>//=====================================================================//
/*! @file
    @brief  RX65N SIDE
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include "common/renesas.hpp"
#include "common/fixed_fifo.hpp"
#include "common/sci_io.hpp"
#include "common/format.hpp"
#include "common/command.hpp"
#include "common/spi_io2.hpp"
#include "common/sdc_man.hpp"
#include "common/tpu_io.hpp"
#include "common/audio_out.hpp"

#include "chip/FAMIPAD.hpp"

#include "spinv.hpp"

namespace {

	typedef device::PORT<device::PORT7, device::bitpos::B0> LED;
// オーディオの D/A として使用
//	typedef device::PORT<device::PORT0, device::bitpos::B5> SW2;

	// Famicon PAD (CMOS 4021B Shift Register)
	// 電源は、微小なので、接続を簡単に行う為、ポートを使う
	typedef device::PORT<device::PORT6, device::bitpos::B0> PAD_VCC;
	typedef device::PORT<device::PORT6, device::bitpos::B1> PAD_GND;
	typedef device::PORT<device::PORT6, device::bitpos::B2> PAD_P_S;
	typedef device::PORT<device::PORT6, device::bitpos::B5> PAD_CLK;
	typedef device::PORT<device::PORT7, device::bitpos::B3> PAD_OUT;
	typedef chip::FAMIPAD<PAD_P_S, PAD_CLK, PAD_OUT> FAMIPAD;
	FAMIPAD		famipad_;

	typedef device::system_io<12000000> SYSTEM_IO;

	typedef utils::fixed_fifo<char, 512>  RECV_BUFF;
	typedef utils::fixed_fifo<char, 1024> SEND_BUFF;
	typedef device::sci_io<device::SCI9, RECV_BUFF, SEND_BUFF> SCI;
	SCI		sci_;

	typedef device::PORT<device::PORT6, device::bitpos::B3> LCD_DISP;
	typedef device::PORT<device::PORT6, device::bitpos::B6> LCD_LIGHT;
	typedef device::glcdc_io<device::GLCDC, 480, 272, device::PIX_TYPE::RGB565> GLCDC_IO;
	GLCDC_IO	glcdc_io_;

	// カード電源制御は使わない場合、「device::NULL_PORT」を指定する。
//	typedef device::PORT<device::PORT6, device::bitpos::B4> SDC_POWER;
	typedef device::NULL_PORT SDC_POWER;

#ifdef SDHI_IF
	typedef fatfs::sdhi_io<device::SDHI, SDC_POWER> SDHI;
	SDHI	sdh_;
#else
	// Soft SDC 用　SPI 定義（SPI）
	typedef device::PORT<device::PORT2, device::bitpos::B2> MISO;  // DAT0
	typedef device::PORT<device::PORT2, device::bitpos::B0> MOSI;  // CMD
	typedef device::PORT<device::PORT2, device::bitpos::B1> SPCK;  // CLK

	typedef device::spi_io2<MISO, MOSI, SPCK> SPI;  ///< Soft SPI 定義

	SPI		spi_;

	typedef device::PORT<device::PORT1, device::bitpos::B7> SDC_SELECT;  // DAT3 カード選択信号
	typedef device::PORT<device::PORT2, device::bitpos::B5> SDC_DETECT;  // CD   カード検出

	typedef fatfs::mmc_io<SPI, SDC_SELECT, SDC_POWER, SDC_DETECT> MMC;   // ハードウェアー定義

	MMC		sdh_(spi_, 20000000);
#endif
	typedef utils::sdc_man SDC;
	SDC		sdc_;

	volatile uint32_t	wpos_;

	/// DMAC 終了割り込み
	class dmac_term_task {
	public:
		void operator() () {
			device::DMAC0::DMCNT.DTE = 1;  // DMA を再スタート
			wpos_ = 0;
		}
	};

	typedef device::dmac_mgr<device::DMAC0, dmac_term_task> DMAC_MGR;
	DMAC_MGR	dmac_mgr_;

	uint32_t get_wave_pos_() { return (dmac_mgr_.get_count() & 0x3ff) ^ 0x3ff; }

	typedef device::R12DA DAC;
	typedef device::dac_out<DAC> DAC_OUT;
	DAC_OUT		dac_out_;

	typedef utils::audio_out<1024, 256> AUDIO_OUT;
	AUDIO_OUT	audio_out_;

	class tpu_task {
	public:
		void operator() () {
			uint32_t tmp = wpos_;
			++wpos_;
			if((tmp ^ wpos_) & 64) {
				audio_out_.service(64);
			}
		}
	};

	typedef device::tpu_io<device::TPU0, tpu_task> TPU0;
	TPU0		tpu0_;

	typedef emu::spinv SPINV;
	SPINV		spinv_;

	utils::command<256> cmd_;

	bool check_mount_() {
		auto f = sdc_.get_mount();
		if(!f) {
			utils::format("SD card not mount.\n");
		}
		return f;
	}

	void command_()
	{
		if(!cmd_.service()) {
			return;
		}

		uint8_t cmdn = cmd_.get_words();
		if(cmdn >= 1) {
			bool f = false;
			if(cmd_.cmp_word(0, "dir")) {  // dir [xxx]
				if(check_mount_()) {
					if(cmdn >= 2) {
						char tmp[128];
						cmd_.get_word(1, sizeof(tmp), tmp);
						sdc_.dir(tmp);
					} else {
						sdc_.dir("");
					}
				}
				f = true;
			} else if(cmd_.cmp_word(0, "cd")) {  // cd [xxx]
				if(check_mount_()) {
					if(cmdn >= 2) {
						char tmp[128];
						cmd_.get_word(1, sizeof(tmp), tmp);
						sdc_.cd(tmp);
					} else {
						sdc_.cd("/");
					}
				}
				f = true;
			} else if(cmd_.cmp_word(0, "pwd")) { // pwd
				utils::format("%s\n") % sdc_.get_current();
				f = true;
			} else if(cmd_.cmp_word(0, "help")) {
				utils::format("    dir [path]\n");
				utils::format("    cd [path]\n");
				utils::format("    pwd\n");
				f = true;
			}
			if(!f) {
				char tmp[128];
				if(cmd_.get_word(0, sizeof(tmp), tmp)) {
					utils::format("Command error: '%s'\n") % tmp;
				}
			}
		}
	}
}

extern "C" {

	uint8_t get_fami_pad()
	{
		return famipad_.update();
	}

	void set_sample_rate(uint32_t freq)
	{
		uint8_t intr_level = 5;
		if(!tpu0_.start(freq, intr_level)) {
			utils::format("TPU0 start error...\n");
		}
	}

	void sci_putch(char ch)
	{
		sci_.putch(ch);
	}

	void sci_puts(const char* str)
	{
		sci_.puts(str);
	}

	char sci_getch(void)
	{
		return sci_.getch();
	}

	uint16_t sci_length()
	{
		return sci_.recv_length();
	}

	DSTATUS disk_initialize(BYTE drv) {
		return sdh_.disk_initialize(drv);
	}

	DSTATUS disk_status(BYTE drv) {
		return sdh_.disk_status(drv);
	}

	DRESULT disk_read(BYTE drv, BYTE* buff, DWORD sector, UINT count) {
		return sdh_.disk_read(drv, buff, sector, count);
	}

	DRESULT disk_write(BYTE drv, const BYTE* buff, DWORD sector, UINT count) {
		return sdh_.disk_write(drv, buff, sector, count);
	}

	DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void* buff) {
		return sdh_.disk_ioctl(drv, ctrl, buff);
	}

	DWORD get_fattime(void) {
		time_t t = 0;
///		rtc_.get_time(t);
		return utils::str::get_fattime(t);
	}

	void utf8_to_sjis(const char* src, char* dst, uint32_t len) {
		utils::str::utf8_to_sjis(src, dst, len);
	}

	int fatfs_get_mount() {
		return sdc_.get_mount();
	}

	int make_full_path(const char* src, char* dst, uint16_t len)
	{
		return sdc_.make_full_path(src, dst, len);
	}
}

int main(int argc, char** argv);

int main(int argc, char** argv)
{
	SYSTEM_IO::setup_system_clock();

	{  // 内臓１２ビット D/A の設定
		bool amp_ena = true;
		dac_out_.start(DAC_OUT::output::CH0_CH1, amp_ena);
		dac_out_.out0(0x8000);
		dac_out_.out1(0x8000);
	}

	{  // SCI 設定
		static const uint8_t sci_level = 2;
		sci_.start(115200, sci_level);
	}

	{  // SD カード・クラスの初期化
		sdh_.start();
		sdc_.start();
	}

	// 波形メモリーの無音状態初期化
	audio_out_.mute();

	{  // サンプリング・タイマー設定
		set_sample_rate(11127);
	}

	{  // DMAC マネージャー開始
		uint8_t intr_level = 4;
		auto ret = dmac_mgr_.start(tpu0_.get_intr_vec(), DMAC_MGR::trans_type::SP_DN_32,
			reinterpret_cast<uint32_t>(audio_out_.get_wave()), DAC::DADR0.address(),
			audio_out_.size(), intr_level);
		if(!ret) {
			utils::format("DMAC Not start...\n");
		}
	}

	utils::format("\rRTK5RX65N Start for Space Invaders\n");

	cmd_.set_prompt("# ");

	{  // GLCDC 初期化
		LCD_DISP::DIR  = 1;
		LCD_LIGHT::DIR = 1;
		LCD_DISP::P  = 0;  // DISP Disable
		LCD_LIGHT::P = 0;  // BackLight Disable (No PWM)
		if(glcdc_io_.start()) {
			utils::format("Start GLCDC\n");
			LCD_DISP::P  = 1;  // DISP Enable
			LCD_LIGHT::P = 1;  // BackLight Enable (No PWM)
			if(!glcdc_io_.control(GLCDC_IO::control_cmd::START_DISPLAY)) {
				utils::format("GLCDC ctrl fail...\n");
			}
		} else {
			utils::format("Fail GLCDC\n");
		}
	}

	LED::DIR = 1;

	{
		PAD_VCC::DIR = 1;
		PAD_VCC::P = 1;
		PAD_GND::DIR = 1;
		PAD_GND::P = 0;
		famipad_.start();
	}

	uint32_t delay_inv = 120;
	uint8_t n = 0;
	while(1) {
		glcdc_io_.sync_vpos();

		if(delay_inv > 0) {
			--delay_inv;
			if(delay_inv == 0) {
				if(sdc_.get_mount()) {
					if(spinv_.start("/inv_roms", "/inv_wavs")) {
						utils::format("Space Invaders start...\n");
					} else {
						utils::format("Space Invaders not start...(fail)\n");
					}
				} else {
					delay_inv = 60;
					utils::format("SD card not mount\n");
				}
			}
		} else {
			spinv_.service((void*)0x00000000, glcdc_io_.get_xsize(), glcdc_io_.get_ysize());

			SPINV::SND_MGR& snd = spinv_.at_sound();
			uint32_t len = snd.get_length();
			const int16_t* wav = snd.get_buffer();
			for(uint32_t i = 0; i < len; ++i) {
				while((audio_out_.at_fifo().size() - audio_out_.at_fifo().length()) < 8) {
				}
				audio::wave_t t;
				t.l_ch = t.r_ch = *wav++;
				audio_out_.at_fifo().put(t);
			}
		}

		sdc_.service(sdh_.service());

		command_();

		++n;
		if(n >= 30) {
			n = 0;
		}
		if(n < 10) {
			LED::P = 0;
		} else {
			LED::P = 1;
		}
	}
}
<|endoftext|>


<commit_before>/*
 * Radiation Watch Pocket Geiger Type 5 library for Arduino.
 *
 * Documentation and usage at:
 * https://github.com/MonsieurV/RadiationWatch
 *
 * Released under MIT License. See LICENSE file.
 *
 * Contributed by:
 * Radiation Watch <http://www.radiation-watch.org/>
 * thomasaw <https://github.com/thomasaw>
 * Tourmal <https://github.com/Toumal>
 * Yoan Tournade <yoan@ytotech.com>
 */
#include "RadiationWatch.h"
#if !defined(ARDUINO_ARCH_AVR) && !defined(ESP8266)
#include <avr/dtostrf.h>
#endif

int volatile _radiationCount = 0;
int volatile _noiseCount = 0;
// Message buffer for output.
char _msg[60];

void _onRadiationHandler()
{
  _radiationCount++;
}

void _onNoiseHandler()
{
  _noiseCount++;
}

RadiationWatch::RadiationWatch(byte signPin, byte noisePin):
    _signPin(signPin), _noisePin(noisePin)
{
  previousTime = 0;
  previousHistoryTime = 0;
  csvStartTime = 0;
  _count = 0;
  historyIndex = 0;
  historyLength = 0;
  _radiationCallback = NULL;
  _noiseCallback = NULL;
}

void RadiationWatch::setup()
{
  pinMode(_signPin, INPUT_PULLUP);
  pinMode(_noisePin, INPUT_PULLUP);
  // Initialize _countHistory[].
  for(int i = 0; i < HISTORY_LENGTH; i++)
    _countHistory[i] = 0;
  // Init measurement time.
  previousTime = millis();
  previousHistoryTime = millis();
  csvStartTime = millis();
  // Attach interrupt handlers.
  attachInterrupt(digitalPinToInterrupt(_signPin), _onRadiationHandler, FALLING);
  attachInterrupt(digitalPinToInterrupt(_noisePin), _onNoiseHandler, RISING);
}

void RadiationWatch::loop()
{
  // Process radiation dose if the process period has elapsed.
  unsigned long currentTime = millis();
  if(currentTime - previousTime >= PROCESS_PERIOD) {
    noInterrupts();
    int currentCount = _radiationCount;
    int currentNoiseCount = _noiseCount;
    _radiationCount = 0;
    _noiseCount = 0;
    interrupts();
    if(currentNoiseCount == 0) {
      // Store count log.
      _countHistory[historyIndex] += currentCount;
      // Add number of counts.
      _count += currentCount;
    }
    // Shift an array for counting log for each 6 seconds.
    if(currentTime - previousHistoryTime >= HISTORY_UNIT * 1000) {
      previousHistoryTime += (unsigned long)(HISTORY_UNIT * 1000);
      historyIndex = (historyIndex + 1) % HISTORY_LENGTH;
      if(historyLength < (HISTORY_LENGTH-1)) {
        // Since, we overwrite the oldest value in the history,
        // the effective maximum length is HISTORY_LENGTH-1
        historyLength++;
      }
      _count -= _countHistory[historyIndex];
      _countHistory[historyIndex] = 0;
    }
    // Save time of current process period
    previousTime = currentTime;
    // Enable the callbacks.
    if(_noiseCallback && currentNoiseCount > 0) {
      _noiseCallback();
    }
    if(_radiationCallback && currentCount > 0) {
      _radiationCallback();
    }
  }
}

void RadiationWatch::registerRadiationCallback(void (*callback)(void))
{
  _radiationCallback = callback;
}

void RadiationWatch::registerNoiseCallback(void (*callback)(void))
{
  _noiseCallback = callback;
}

char* RadiationWatch::csvKeys()
{
  // CSV-formatting for output.
  return "time(ms),count,cpm,uSv/h,uSv/hError";
}

char* RadiationWatch::csvStatus()
{
  // Format message. We use dtostrf() to format float to string.
  char cpmBuff[8];
  char uSvBuff[8];
  char uSvdBuff[8];
  dtostrf(cpm(), -1, 3, cpmBuff);
  dtostrf(uSvh(), -1, 3, uSvBuff);
  dtostrf(uSvhError(), -1, 3, uSvdBuff);
  sprintf(_msg, "%lu,%d,%s,%s,%s",
          duration(), currentRadiationCount(), cpmBuff, uSvBuff, uSvdBuff);
  return _msg;
}

unsigned long RadiationWatch::duration()
{
  // Elapsed time of measurement (milliseconds).
  // Will overflow after days 49 of measurement.
  return previousTime - csvStartTime;
}

int RadiationWatch::currentRadiationCount() {
  noInterrupts();
  int currentCount = _radiationCount;
  interrupts();
  return currentCount;
}

float RadiationWatch::cpm()
{
  // cpm = uSv x alpha
  float min = cpmTime();
  return (min > 0) ? _count / min : 0;
}

static const float kAlpha = 53.032;

float RadiationWatch::uSvh()
{
  return cpm() / kAlpha;
}

float RadiationWatch::uSvhError()
{
  float min = cpmTime();
  return (min > 0) ? sqrt(_count) / min / kAlpha : 0;
}
<commit_msg>RadiationWatch.cpp: let _onNoiseHandler trigger at FALLING<commit_after>/*
 * Radiation Watch Pocket Geiger Type 5 library for Arduino.
 *
 * Documentation and usage at:
 * https://github.com/MonsieurV/RadiationWatch
 *
 * Released under MIT License. See LICENSE file.
 *
 * Contributed by:
 * Radiation Watch <http://www.radiation-watch.org/>
 * thomasaw <https://github.com/thomasaw>
 * Tourmal <https://github.com/Toumal>
 * Yoan Tournade <yoan@ytotech.com>
 */
#include "RadiationWatch.h"
#if !defined(ARDUINO_ARCH_AVR) && !defined(ESP8266)
#include <avr/dtostrf.h>
#endif

int volatile _radiationCount = 0;
int volatile _noiseCount = 0;
// Message buffer for output.
char _msg[60];

void _onRadiationHandler()
{
  _radiationCount++;
}

void _onNoiseHandler()
{
  _noiseCount++;
}

RadiationWatch::RadiationWatch(byte signPin, byte noisePin):
    _signPin(signPin), _noisePin(noisePin)
{
  previousTime = 0;
  previousHistoryTime = 0;
  csvStartTime = 0;
  _count = 0;
  historyIndex = 0;
  historyLength = 0;
  _radiationCallback = NULL;
  _noiseCallback = NULL;
}

void RadiationWatch::setup()
{
  pinMode(_signPin, INPUT_PULLUP);
  pinMode(_noisePin, INPUT_PULLUP);
  // Initialize _countHistory[].
  for(int i = 0; i < HISTORY_LENGTH; i++)
    _countHistory[i] = 0;
  // Init measurement time.
  previousTime = millis();
  previousHistoryTime = millis();
  csvStartTime = millis();
  // Attach interrupt handlers.
  attachInterrupt(digitalPinToInterrupt(_signPin), _onRadiationHandler, FALLING);
  attachInterrupt(digitalPinToInterrupt(_noisePin), _onNoiseHandler, FALLING);
}

void RadiationWatch::loop()
{
  // Process radiation dose if the process period has elapsed.
  unsigned long currentTime = millis();
  if(currentTime - previousTime >= PROCESS_PERIOD) {
    noInterrupts();
    int currentCount = _radiationCount;
    int currentNoiseCount = _noiseCount;
    _radiationCount = 0;
    _noiseCount = 0;
    interrupts();
    if(currentNoiseCount == 0) {
      // Store count log.
      _countHistory[historyIndex] += currentCount;
      // Add number of counts.
      _count += currentCount;
    }
    // Shift an array for counting log for each 6 seconds.
    if(currentTime - previousHistoryTime >= HISTORY_UNIT * 1000) {
      previousHistoryTime += (unsigned long)(HISTORY_UNIT * 1000);
      historyIndex = (historyIndex + 1) % HISTORY_LENGTH;
      if(historyLength < (HISTORY_LENGTH-1)) {
        // Since, we overwrite the oldest value in the history,
        // the effective maximum length is HISTORY_LENGTH-1
        historyLength++;
      }
      _count -= _countHistory[historyIndex];
      _countHistory[historyIndex] = 0;
    }
    // Save time of current process period
    previousTime = currentTime;
    // Enable the callbacks.
    if(_noiseCallback && currentNoiseCount > 0) {
      _noiseCallback();
    }
    if(_radiationCallback && currentCount > 0) {
      _radiationCallback();
    }
  }
}

void RadiationWatch::registerRadiationCallback(void (*callback)(void))
{
  _radiationCallback = callback;
}

void RadiationWatch::registerNoiseCallback(void (*callback)(void))
{
  _noiseCallback = callback;
}

char* RadiationWatch::csvKeys()
{
  // CSV-formatting for output.
  return "time(ms),count,cpm,uSv/h,uSv/hError";
}

char* RadiationWatch::csvStatus()
{
  // Format message. We use dtostrf() to format float to string.
  char cpmBuff[8];
  char uSvBuff[8];
  char uSvdBuff[8];
  dtostrf(cpm(), -1, 3, cpmBuff);
  dtostrf(uSvh(), -1, 3, uSvBuff);
  dtostrf(uSvhError(), -1, 3, uSvdBuff);
  sprintf(_msg, "%lu,%d,%s,%s,%s",
          duration(), currentRadiationCount(), cpmBuff, uSvBuff, uSvdBuff);
  return _msg;
}

unsigned long RadiationWatch::duration()
{
  // Elapsed time of measurement (milliseconds).
  // Will overflow after days 49 of measurement.
  return previousTime - csvStartTime;
}

int RadiationWatch::currentRadiationCount() {
  noInterrupts();
  int currentCount = _radiationCount;
  interrupts();
  return currentCount;
}

float RadiationWatch::cpm()
{
  // cpm = uSv x alpha
  float min = cpmTime();
  return (min > 0) ? _count / min : 0;
}

static const float kAlpha = 53.032;

float RadiationWatch::uSvh()
{
  return cpm() / kAlpha;
}

float RadiationWatch::uSvhError()
{
  float min = cpmTime();
  return (min > 0) ? sqrt(_count) / min / kAlpha : 0;
}
<|endoftext|>