void TouchInputDevice::sync(EmuInput *emuInput)
{
	if (emuFunction() <= 0)
		return;

	if (emuFunction() <= 2) { // Pad
		if (!m_converted) {
			convertPad();
			m_converted = true;
		}
		int padIndex = emuFunction() - 1;
		emuInput->pad[padIndex].setButtons(m_buttons);
	} else if (emuFunction() <= 4) { // Mouse
		if (!m_converted) {
			convertMouse();
			m_converted = true;
		}
		int xRel = m_mouseX - m_lastMouseX;
		int yRel = m_mouseY - m_lastMouseY;
		int mouseIndex = emuFunction() - 3;
		emuInput->mouse[mouseIndex].setButtons(m_buttons);
		emuInput->mouse[mouseIndex].addRel(xRel, yRel);
	} else if (emuFunction() == 5) { // Touch
		if (!m_converted) {
			convertTouch();
			m_converted = true;
		}
		emuInput->touch.setPos(m_touchPointInEmu.x(), m_touchPointInEmu.y());
	}
}
QString KicadModule2Svg::convert(const QString & filename, const QString & moduleName, bool allowPadsAndPins) 
{
	m_nonConnectorNumber = 0;
	initLimits();

	QFile file(filename);
	if (!file.open(QFile::ReadOnly)) {
		throw QObject::tr("unable to open %1").arg(filename);
	}

	QString text;
	QTextStream textStream(&file);

	QString metadata = makeMetadata(filename, "module", moduleName);


	bool gotModule = false;
	while (true) {
		QString line = textStream.readLine();
		if (line.isNull()) {
			break;
		}

		if (line.contains("$MODULE") && line.contains(moduleName, Qt::CaseInsensitive)) {
			gotModule = true;
			break;
		}
	}

	if (!gotModule) {
		throw QObject::tr("footprint %1 not found in %2").arg(moduleName).arg(filename);
	}

	bool gotT0;
	QString line;
	while (true) {
		line = textStream.readLine();
		if (line.isNull()) {
			throw QObject::tr("unexpected end of file in footprint %1 in file %2").arg(moduleName).arg(filename);
		}

		if (line.startsWith("T0") || line.startsWith("DS") || line.startsWith("DA") || line.startsWith("DC")) {
			gotT0 = true;
			break;
		}
		else if (line.startsWith("Cd")) {
			metadata += m_comment.arg(TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(line.remove(0,3))));
		}
		else if (line.startsWith("Kw")) {
			QStringList keywords = line.split(" ");
			for (int i = 1; i < keywords.count(); i++) {
				metadata += m_attribute.arg("keyword").arg(TextUtils::stripNonValidXMLCharacters(TextUtils::escapeAnd(keywords[i])));
			}
		}
	}

	metadata += endMetadata();

	if (!gotT0) {
		throw QObject::tr("unexpected format (1) in %1 from %2").arg(moduleName).arg(filename);
	}

	while (line.startsWith("T")) {
		line = textStream.readLine();
		if (line.isNull()) {
			throw QObject::tr("unexpected end of file in footprint %1 in file %2").arg(moduleName).arg(filename);
		}
	}

	bool done = false;
	QString copper0;
	QString copper1;
	QString silkscreen0;
	QString silkscreen1;

	while (true) {
		if (line.startsWith("$PAD")) break;
		if (line.startsWith("$EndMODULE")) {
			done = true;
			break;
		}

		int layer = 0;
		QString svgElement;
		if (line.startsWith("DS")) {
			layer = drawDSegment(line, svgElement);
		}
		else if (line.startsWith("DA")) {
			layer = drawDArc(line, svgElement);
		}
		else if (line.startsWith("DC")) {
			layer = drawDCircle(line, svgElement);
		}
		switch (layer) {
			case KicadSilkscreenTop:
				silkscreen1 += svgElement;
				break;
			case KicadSilkscreenBottom:
				silkscreen0 += svgElement;
				break;
			default:
				break;
		}
	
		line = textStream.readLine();
		if (line.isNull()) {
			throw QObject::tr("unexpected end of file in footprint %1 in file %2").arg(moduleName).arg(filename);
		}
	}

	if (!done) {
		QList<int> numbers;
		for (int i = 0; i < 512; i++) {
			numbers << i;
		}
		int pads = 0;
		int pins = 0;
		while (!done) {
			try {
				QString pad;
				PadLayer padLayer = convertPad(textStream, pad, numbers);
				switch (padLayer) {
					case ToCopper0:
						copper0 += pad;
						pins++;
						break;
					case ToCopper1:
						copper1 += pad;
						pads++;
						break;
					default:
						break;
				}
			}
			catch (const QString & msg) {
				DebugDialog::debug(QString("kicad pad %1 conversion failed in %2: %3").arg(moduleName).arg(filename).arg(msg));
			}

			while (true) {
				line = textStream.readLine();
				if (line.isNull()) {
					throw QObject::tr("unexpected end of file in footprint %1 in file %2").arg(moduleName).arg(filename);
				}

				if (line.contains("$SHAPE3D")) {
					done = true;
					break;
				}
				if (line.contains("$EndMODULE")) {
					done = true;
					break;
				}
				if (line.contains("$PAD")) {
					break;
				}
			}
		}

		if (!allowPadsAndPins && pins > 0 && pads > 0) {
			throw QObject::tr("Sorry, Fritzing can't yet handle both pins and pads together (in %1 in %2)").arg(moduleName).arg(filename);
		}

	}

	if (!copper0.isEmpty()) {
		copper0 = offsetMin("\n<g id='copper0'><g id='copper1'>" + copper0 + "</g></g>\n");
	}
	if (!copper1.isEmpty()) {
		copper1 = offsetMin("\n<g id='copper1'>" + copper1 + "</g>\n");
	}
	if (!silkscreen1.isEmpty()) {
		silkscreen1 = offsetMin("\n<g id='silkscreen'>" + silkscreen1 + "</g>\n");
	}
	if (!silkscreen0.isEmpty()) {
		silkscreen0 = offsetMin("\n<g id='silkscreen0'>" + silkscreen0 + "</g>\n");
	}

	QString svg = TextUtils::makeSVGHeader(10000, 10000, m_maxX - m_minX, m_maxY - m_minY) 
					+ m_title + m_description + metadata + copper0 + copper1 + silkscreen0 + silkscreen1 + "</svg>";

	return svg;
}