From 955c4589ca254814ae7ec6ed911e0a62febc5da1 Mon Sep 17 00:00:00 2001
From: dknn <yoann.blein@free.fr>
Date: Fri, 10 Aug 2012 20:04:56 +0200
Subject: Capture and send user UI events


diff --git a/Swift/QtUI/ScreenSharing/RemoteScreenViewerWidget.cpp b/Swift/QtUI/ScreenSharing/RemoteScreenViewerWidget.cpp
index 899f335..91a515f 100644
--- a/Swift/QtUI/ScreenSharing/RemoteScreenViewerWidget.cpp
+++ b/Swift/QtUI/ScreenSharing/RemoteScreenViewerWidget.cpp
@@ -20,6 +20,8 @@ namespace Swift {
 RemoteScreenViewerWidget::RemoteScreenViewerWidget(boost::shared_ptr<IncomingScreenSharing> incScreenSharing, QWidget *parent) :
 	QWidget(parent), iss(incScreenSharing)
 {
+	setMouseTracking(true);
+	setFocusPolicy(Qt::StrongFocus);
 	iss->onNewImageReceived.connect(boost::bind(&RemoteScreenViewerWidget::handleNewImageReceived, this, _1));
 }
 
@@ -28,7 +30,7 @@ RemoteScreenViewerWidget::~RemoteScreenViewerWidget()
 	iss->onNewImageReceived.disconnect(boost::bind(&RemoteScreenViewerWidget::handleNewImageReceived, this, _1));
 }
 
-void RemoteScreenViewerWidget::paintEvent(QPaintEvent* event)
+void RemoteScreenViewerWidget::paintEvent(QPaintEvent*)
 {
 	if (!pixmap.isNull()) {
 		QPainter painter(this);
@@ -37,18 +39,94 @@ void RemoteScreenViewerWidget::paintEvent(QPaintEvent* event)
 	}
 }
 
-void RemoteScreenViewerWidget::resizeEvent(QResizeEvent *event)
+void RemoteScreenViewerWidget::resizeEvent(QResizeEvent* event)
 {
 	if (!pixmap.isNull()) {
-		pixmap = pixmap.scaled(event->size(), Qt::KeepAspectRatio);
+		pixmap = pixmap.scaled(event->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
 	}
 	QWidget::resizeEvent(event);
 }
 
+void RemoteScreenViewerWidget::keyPressEvent(QKeyEvent* event)
+{
+	InputEventPayload::Event ev(InputEventPayload::Event::KeyPress, event->key());
+	iss->sendInputEvent(ev);
+	QWidget::keyPressEvent(event);
+}
+
+void RemoteScreenViewerWidget::keyReleaseEvent(QKeyEvent* event)
+{
+	InputEventPayload::Event ev(InputEventPayload::Event::KeyRelease, event->key());
+	iss->sendInputEvent(ev);
+	QWidget::keyReleaseEvent(event);
+}
+
+void RemoteScreenViewerWidget::mouseMoveEvent(QMouseEvent* event)
+{
+	float x = static_cast<float>(event->x()) / pixmap.width();
+	float y = static_cast<float>(event->y()) / pixmap.height();
+	InputEventPayload::Event ev(InputEventPayload::Event::MouseMove, x, y);
+	iss->sendInputEvent(ev);
+	QWidget::mouseMoveEvent(event);
+}
+
+void RemoteScreenViewerWidget::mousePressEvent(QMouseEvent* event)
+{
+	int btnId = 0;
+	switch (event->button()) {
+		case Qt::LeftButton:
+			btnId = 3;
+			break;
+		case Qt::MidButton:
+			btnId = 2;
+			break;
+		case Qt::RightButton:
+			btnId = 1;
+			break;
+		default:
+			break;
+	}
+	if (btnId) {
+		InputEventPayload::Event ev(InputEventPayload::Event::MousePress, btnId);
+		iss->sendInputEvent(ev);
+	}
+	QWidget::mousePressEvent(event);
+}
+
+void RemoteScreenViewerWidget::mouseReleaseEvent(QMouseEvent* event)
+{
+	int btnId = 0;
+	switch (event->button()) {
+		case Qt::LeftButton:
+			btnId = 3;
+			break;
+		case Qt::MidButton:
+			btnId = 2;
+			break;
+		case Qt::RightButton:
+			btnId = 1;
+			break;
+		default:
+			break;
+	}
+	if (btnId) {
+		InputEventPayload::Event ev(InputEventPayload::Event::MouseRelease, btnId);
+		iss->sendInputEvent(ev);
+	}
+	QWidget::mouseReleaseEvent(event);
+}
+
+void RemoteScreenViewerWidget::wheelEvent(QWheelEvent* event)
+{
+	InputEventPayload::Event ev(InputEventPayload::Event::MouseWheel, event->delta());
+	iss->sendInputEvent(ev);
+	QWidget::wheelEvent(event);
+}
+
 void RemoteScreenViewerWidget::handleNewImageReceived(const Image& image)
 {
 	QImage qImg(image.data.data(), image.width, image.height, QImage::Format_RGB888);
-	pixmap = QPixmap::fromImage(qImg).scaled(size(), Qt::KeepAspectRatio);
+	pixmap = QPixmap::fromImage(qImg).scaled(size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
 	update();
 }
 
diff --git a/Swift/QtUI/ScreenSharing/RemoteScreenViewerWidget.h b/Swift/QtUI/ScreenSharing/RemoteScreenViewerWidget.h
index b0a377e..2738e98 100644
--- a/Swift/QtUI/ScreenSharing/RemoteScreenViewerWidget.h
+++ b/Swift/QtUI/ScreenSharing/RemoteScreenViewerWidget.h
@@ -21,8 +21,14 @@ namespace Swift {
 			~RemoteScreenViewerWidget();
 
 		protected:
-			void paintEvent(QPaintEvent* event);
+			void paintEvent(QPaintEvent*);
 			void resizeEvent(QResizeEvent* event);
+			void keyPressEvent(QKeyEvent* event);
+			void keyReleaseEvent(QKeyEvent* event);
+			void mouseMoveEvent(QMouseEvent* event);
+			void mousePressEvent(QMouseEvent* event);
+			void mouseReleaseEvent(QMouseEvent* event);
+			void wheelEvent(QWheelEvent* event);
 
 		private:
 			void handleNewImageReceived(const Image& image);
diff --git a/Swiften/Base/FloatCompare.h b/Swiften/Base/FloatCompare.h
new file mode 100644
index 0000000..152ffff
--- /dev/null
+++ b/Swiften/Base/FloatCompare.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <cmath>
+#include <limits>
+
+namespace Swift {
+	bool approximatelyEqual(float a, float b, float epsilon = std::numeric_limits<float>::epsilon()) {
+		return std::fabs(a - b) <= ( (std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * epsilon);
+	}
+
+	bool essentiallyEqual(float a, float b, float epsilon = std::numeric_limits<float>::epsilon()) {
+		return std::fabs(a - b) <= ( (std::fabs(a) > std::fabs(b) ? std::fabs(b) : std::fabs(a)) * epsilon);
+	}
+
+	bool definitelyGreaterThan(float a, float b, float epsilon = std::numeric_limits<float>::epsilon()) {
+		return (a - b) > ( (std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * epsilon);
+	}
+
+	bool definitelyLessThan(float a, float b, float epsilon = std::numeric_limits<float>::epsilon()) {
+		return (b - a) > ( (std::fabs(a) < std::fabs(b) ? std::fabs(b) : std::fabs(a)) * epsilon);
+	}
+}
diff --git a/Swiften/Elements/InputEventPayload.cpp b/Swiften/Elements/InputEventPayload.cpp
new file mode 100644
index 0000000..411d2cd
--- /dev/null
+++ b/Swiften/Elements/InputEventPayload.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Elements/InputEventPayload.h>
+
+namespace Swift {
+
+InputEventPayload::InputEventPayload()
+	: action_(InputEventPayload::Notify)
+{
+}
+
+void InputEventPayload::addEvent(const InputEventPayload::Event& event)
+{
+	events_.push_back(event);
+}
+
+const std::vector<InputEventPayload::Event>& InputEventPayload::getEvents() const
+{
+	return events_;
+}
+
+void InputEventPayload::setAction(InputEventPayload::Action action)
+{
+	action_ = action;
+}
+
+InputEventPayload::Action InputEventPayload::getAction() const
+{
+	return action_;
+}
+
+}
diff --git a/Swiften/Elements/InputEventPayload.h b/Swiften/Elements/InputEventPayload.h
new file mode 100644
index 0000000..1ccdec8
--- /dev/null
+++ b/Swiften/Elements/InputEventPayload.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/Payload.h>
+
+#include <boost/shared_ptr.hpp>
+#include <vector>
+
+namespace Swift {
+	class InputEventPayload : public Payload {
+		public:
+			typedef boost::shared_ptr<InputEventPayload> ref;
+
+			enum Action {
+				Notify,
+				Unknown,
+			};
+
+			struct Event {
+				enum EventType {
+					MouseMove,
+					MouseWheel,
+					MousePress,
+					MouseRelease,
+					KeyType,
+					KeyPress,
+					KeyRelease,
+					Unknown,
+				};
+
+				Event(EventType eventType = Unknown)
+					: type(eventType), integerArg(0), realArg1(0), realArg2(0) {}
+				Event(EventType eventType, int integerArg)
+					: type(eventType), integerArg(integerArg), realArg1(0), realArg2(0) {}
+				Event(EventType eventType, float realArg1, float realArg2)
+					: type(eventType), integerArg(0), realArg1(realArg1), realArg2(realArg2) {}
+
+				EventType type;
+				int integerArg;
+				float realArg1;
+				float realArg2;
+			};
+
+		public:
+			InputEventPayload();
+
+			void addEvent(const Event& event);
+			const std::vector<Event>& getEvents() const;
+
+			void setAction(Action action);
+			Action getAction() const;
+
+		private:
+			Action action_;
+			std::vector<Event> events_;
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index 167f5c1..ec05ef6 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -70,6 +70,7 @@
 #include <Swiften/Parser/PayloadParsers/RTPPayloadTypeParser.h>
 #include <Swiften/Parser/PayloadParsers/JingleRTPDescriptionParserFactory.h>
 #include <Swiften/Parser/PayloadParsers/JingleRawUDPTransportPayloadParser.h>
+#include <Swiften/Parser/PayloadParsers/InputEventParser.h>
 
 using namespace boost;
 
@@ -134,6 +135,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
 	factories_.push_back(boost::make_shared<JingleRTPDescriptionParserFactory>(this));
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<RTPPayloadTypeParser> >("payload-type"));
 	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<JingleRawUDPTransportPayloadParser> >("transport", "urn:xmpp:jingle:transports:raw-udp:1"));
+	factories_.push_back(boost::make_shared<GenericPayloadParserFactory<InputEventParser> >("inputevt", "http://sip-comunicator.org/protocol/inputevt"));
 
 	foreach(shared_ptr<PayloadParserFactory> factory, factories_) {
 		addFactory(factory.get());
diff --git a/Swiften/Parser/PayloadParsers/InputEventParser.cpp b/Swiften/Parser/PayloadParsers/InputEventParser.cpp
new file mode 100644
index 0000000..44d9849
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/InputEventParser.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Parser/PayloadParsers/InputEventParser.h>
+
+#include <boost/optional.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace Swift {
+
+void InputEventParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) {
+	if (level == 0) {
+		// <inputevt> tag
+		InputEventPayload::ref payload = getPayloadInternal();
+		payload->setAction(actionFromString(attributes.getAttributeValue("action").get_value_or("")));
+	} else if (level == 1) {
+		// <remote-control> tag
+	} else if (level == 2) {
+		InputEventPayload::Event event(eventTypeFromString(element));
+		switch (event.type) {
+			case InputEventPayload::Event::MouseMove:
+				try {
+					event.realArg1 = boost::lexical_cast<float>(attributes.getAttributeValue("x").get_value_or("0"));
+					event.realArg2 = boost::lexical_cast<float>(attributes.getAttributeValue("y").get_value_or("0"));
+				} catch (boost::bad_lexical_cast &) {}
+				break;
+			case InputEventPayload::Event::MouseWheel:
+			case InputEventPayload::Event::MousePress:
+			case InputEventPayload::Event::MouseRelease:
+			case InputEventPayload::Event::KeyType:
+			case InputEventPayload::Event::KeyPress:
+			case InputEventPayload::Event::KeyRelease:
+				try {
+					event.integerArg = boost::lexical_cast<int>(attributes.getEntries().front().getValue());
+				} catch (boost::bad_lexical_cast &) {}
+			default:
+				break;
+		}
+	}
+	++level;
+}
+
+void InputEventParser::handleEndElement(const std::string&, const std::string&) {
+	--level;
+}
+
+void InputEventParser::handleCharacterData(const std::string&){
+
+}
+
+InputEventPayload::Action InputEventParser::actionFromString(const std::string& action)
+{
+	if (action == "notify")
+		return InputEventPayload::Notify;
+
+	return InputEventPayload::Unknown;
+}
+
+InputEventPayload::Event::EventType InputEventParser::eventTypeFromString(const std::string& eventType) {
+	if (eventType == "mouse-move")
+		return InputEventPayload::Event::MouseMove;
+	else if (eventType == "mouse-wheel")
+		return InputEventPayload::Event::MouseWheel;
+	else if (eventType == "mouse-press")
+		return InputEventPayload::Event::MousePress;
+	else if (eventType == "mouse-release")
+		return InputEventPayload::Event::MouseRelease;
+	else if (eventType == "key-type")
+		return InputEventPayload::Event::KeyType;
+	else if (eventType == "key-press")
+		return InputEventPayload::Event::KeyPress;
+	else if (eventType == "key-release")
+		return InputEventPayload::Event::KeyRelease;
+
+	return InputEventPayload::Event::Unknown;
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/InputEventParser.h b/Swiften/Parser/PayloadParsers/InputEventParser.h
new file mode 100644
index 0000000..a131254
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/InputEventParser.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/InputEventPayload.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+	class InputEventParser : public GenericPayloadParser<InputEventPayload> {
+		public:
+			virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+			virtual void handleEndElement(const std::string&, const std::string&);
+			virtual void handleCharacterData(const std::string&);
+
+		private:
+			static InputEventPayload::Action actionFromString(const std::string& action);
+			static InputEventPayload::Event::EventType eventTypeFromString(const std::string& eventType);
+
+		private:
+			int level;
+	};
+}
diff --git a/Swiften/Parser/PayloadParsers/JingleParserFactory.h b/Swiften/Parser/PayloadParsers/JingleParserFactory.h
index fa25aeb..9feade7 100644
--- a/Swiften/Parser/PayloadParsers/JingleParserFactory.h
+++ b/Swiften/Parser/PayloadParsers/JingleParserFactory.h
@@ -31,5 +31,3 @@ namespace Swift {
 			
 	};
 }
-
-
diff --git a/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.cpp b/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.cpp
index 7429b39..68893c7 100644
--- a/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.cpp
+++ b/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.cpp
@@ -4,7 +4,7 @@
  * See Documentation/Licenses/BSD-simplified.txt for more information.
  */
 
-#include "JingleRTPDescriptionParser.h"
+#include <Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.h>
 
 #include <Swiften/Parser/PayloadParserFactoryCollection.h>
 #include <Swiften/Parser/PayloadParserFactory.h>
@@ -23,7 +23,7 @@ void JingleRTPDescriptionParser::handleStartElement(const std::string& element,
 		const std::string& media = attributes.getAttributeValue("media").get_value_or("");
 		getPayloadInternal()->setMedia(mediaTypeFromString(media));
 	}
-	
+
 	if (level == 1) {
 		if (element == "bandwidth") {
 			parsingBandwidth = true;
@@ -66,8 +66,8 @@ void JingleRTPDescriptionParser::handleCharacterData(const std::string& data) {
 		bandwidthValue += data;
 	}
 }
-		
-JingleRTPDescription::MediaType JingleRTPDescriptionParser::mediaTypeFromString(const std::string& media) const {
+
+JingleRTPDescription::MediaType JingleRTPDescriptionParser::mediaTypeFromString(const std::string& media) {
 	if (media == "audio") {
 		return JingleRTPDescription::Audio;
 	} else if (media == "video") {
diff --git a/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.h b/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.h
index d250754..6ffad45 100644
--- a/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.h
+++ b/Swiften/Parser/PayloadParsers/JingleRTPDescriptionParser.h
@@ -11,28 +11,25 @@
 #include <Swiften/Parser/PayloadParser.h>
 
 namespace Swift {
-
-class PayloadParserFactoryCollection;
-
-class JingleRTPDescriptionParser : public GenericPayloadParser<JingleRTPDescription> {
-	public:
-		JingleRTPDescriptionParser(PayloadParserFactoryCollection* factories);
-
-		virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
-		virtual void handleEndElement(const std::string& element, const std::string& ns);
-		virtual void handleCharacterData(const std::string& data);	
-
-	private:
-		JingleRTPDescription::MediaType mediaTypeFromString(const std::string& media) const;
-
-	private:
-		PayloadParserFactoryCollection* factories;
-		int level;
-		//CurrentParseElement currentElement;
-		boost::shared_ptr<PayloadParser> currentPayloadParser;
-		bool parsingBandwidth;
-		std::string bandwidthType;
-		std::string bandwidthValue;
-};
-
+	class PayloadParserFactoryCollection;
+
+	class JingleRTPDescriptionParser : public GenericPayloadParser<JingleRTPDescription> {
+		public:
+			JingleRTPDescriptionParser(PayloadParserFactoryCollection* factories);
+
+			virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+			virtual void handleEndElement(const std::string& element, const std::string& ns);
+			virtual void handleCharacterData(const std::string& data);
+
+		private:
+			static JingleRTPDescription::MediaType mediaTypeFromString(const std::string& media);
+
+		private:
+			PayloadParserFactoryCollection* factories;
+			int level;
+			boost::shared_ptr<PayloadParser> currentPayloadParser;
+			bool parsingBandwidth;
+			std::string bandwidthType;
+			std::string bandwidthValue;
+	};
 }
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index 43d50f0..c3b6980 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -76,6 +76,7 @@ sources = [
 		"PayloadParsers/RTPPayloadTypeParser.cpp",
 		"PayloadParsers/JingleRTPDescriptionParser.cpp",
 		"PayloadParsers/JingleRawUDPTransportPayloadParser.cpp",
+		"PayloadParsers/InputEventParser.cpp",
 		"PlatformXMLParserFactory.cpp",
 		"PresenceParser.cpp",
 		"SerializingParser.cpp",
diff --git a/Swiften/SConscript b/Swiften/SConscript
index b8ca1d9..d19334f 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -131,6 +131,7 @@ if env["SCONS_STAGE"] == "build" :
 			"Elements/StreamResumed.cpp",
 			"Elements/VCard.cpp",
 			"Elements/MUCOccupant.cpp",
+			"Elements/InputEventPayload.cpp",
 			"Entity/Entity.cpp",
 			"Entity/PayloadPersister.cpp",
 			"MUC/MUC.cpp",
@@ -207,6 +208,7 @@ if env["SCONS_STAGE"] == "build" :
 			"Serializer/PayloadSerializers/RTPPayloadTypeSerializer.cpp",			
 			"Serializer/PayloadSerializers/JingleRTPDescriptionSerializer.cpp",
 			"Serializer/PayloadSerializers/JingleRawUDPTransportPayloadSerializer.cpp",
+			"Serializer/PayloadSerializers/InputEventSerializer.cpp",
 			"Serializer/PresenceSerializer.cpp",
 			"Serializer/StanzaSerializer.cpp",
 			"Serializer/StreamErrorSerializer.cpp",
diff --git a/Swiften/ScreenSharing/IncomingScreenSharing.cpp b/Swiften/ScreenSharing/IncomingScreenSharing.cpp
index 15574c4..fbd4b16 100644
--- a/Swiften/ScreenSharing/IncomingScreenSharing.cpp
+++ b/Swiften/ScreenSharing/IncomingScreenSharing.cpp
@@ -11,24 +11,33 @@
 #include <Swiften/Elements/JingleRTPDescription.h>
 #include <Swiften/Network/UDPSocketFactory.h>
 #include <Swiften/Network/UDPSocket.h>
+#include <Swiften/Network/TimerFactory.h>
+#include <Swiften/Network/Timer.h>
 #include <Swiften/ScreenSharing/RTPSessionImpl.h>
 #include <Swiften/ScreenSharing/VP8Decoder.h>
 #include <Swiften/ScreenSharing/VP8RTPParser.h>
+#include <Swiften/Queries/Request.h>
+#include <Swiften/Queries/GenericRequest.h>
+#include <Swiften/Base/FloatCompare.h>
 
 #include <boost/bind.hpp>
 
 namespace Swift {
 
 IncomingScreenSharing::IncomingScreenSharing(boost::shared_ptr<JingleSession> session, UDPSocketFactory* udpSocketFactory,
-											 boost::shared_ptr<JingleContentPayload> content)
+											 TimerFactory* timerFactory, IQRouter* iqRouter, boost::shared_ptr<JingleContentPayload> content)
 	: ScreenSharing(session, udpSocketFactory),
-	  initialContent(content), parser(0), decoder(0)
+	  initialContent(content), parser(0), decoder(0), lastMouveMoveEvent(InputEventPayload::Event::MouseMove),
+	  inputEventPayload(boost::make_shared<InputEventPayload>()), eventSendingTimer(timerFactory->createTimer(500)),
+	  iqRouter(iqRouter)
 {
 	onStateChange(ScreenSharing::WaitingForAccept);
+	eventSendingTimer->onTick.connect(boost::bind(&IncomingScreenSharing::handleEventSendingTimerTick, this));
 }
 
 IncomingScreenSharing::~IncomingScreenSharing()
 {
+	eventSendingTimer->onTick.disconnect(boost::bind(&IncomingScreenSharing::handleEventSendingTimerTick, this));
 	delete rtpSession;
 	delete parser;
 	delete decoder;
@@ -73,7 +82,7 @@ void IncomingScreenSharing::accept()
 		decoder = new VP8Decoder;
 		parser = new VP8RTPParser(decoder);
 		rtpSession->onIncomingPacket.connect(boost::bind(&VP8RTPParser::newPayloadReceived, parser, _1, _2, _3));
-		decoder->onNewImageDecoded.connect(boost::bind(&IncomingScreenSharing::hangleNewImageDecoded, this, _1));
+		decoder->onNewImageDecoded.connect(boost::bind(&IncomingScreenSharing::handleNewImageDecoded, this, _1));
 	}
 
 	onStateChange(ScreenSharing::Connecting);
@@ -84,15 +93,60 @@ const JID &IncomingScreenSharing::getSender() const
 	return jingleSession->getInitiator();
 }
 
+void IncomingScreenSharing::sendInputEvent(const InputEventPayload::Event& event)
+{
+	if (event.type == InputEventPayload::Event::Unknown)
+		return;
+
+	if (inputEventPayload->getEvents().empty()) {
+		eventSendingTimer->start();
+	}
+	if (event.type == InputEventPayload::Event::MouseMove) {
+		lastMouveMoveEvent.realArg1 = event.realArg1;
+		lastMouveMoveEvent.realArg2 = event.realArg2;
+	} else {
+		addLastMouseMoveIfDifferent();
+		inputEventPayload->addEvent(event);
+	}
+}
+
 JingleContentID IncomingScreenSharing::getContentID() const
 {
 	return JingleContentID(initialContent->getName(), initialContent->getCreator());
 }
 
-void IncomingScreenSharing::hangleNewImageDecoded(const Image& image)
+void IncomingScreenSharing::handleNewImageDecoded(const Image& image)
 {
 	onStateChange(ScreenSharing::Receiving);
 	onNewImageReceived(image);
 }
 
+void IncomingScreenSharing::handleEventSendingTimerTick()
+{
+	addLastMouseMoveIfDifferent();
+	boost::shared_ptr< GenericRequest<InputEventPayload> > request
+			= boost::make_shared< GenericRequest<InputEventPayload> >(IQ::Set, getSender(), inputEventPayload, iqRouter);
+	request->send();
+	// Prepare for a new payload
+	inputEventPayload.reset(new InputEventPayload);
+}
+
+void IncomingScreenSharing::addLastMouseMoveIfDifferent()
+{
+	const std::vector<InputEventPayload::Event>& events = inputEventPayload->getEvents();
+	std::vector<InputEventPayload::Event>::const_reverse_iterator last;
+	std::vector<InputEventPayload::Event>::const_reverse_iterator it;
+	for (it = events.rbegin(); it != events.rend(); ++it) {
+		if (it->type == InputEventPayload::Event::MouseMove) {
+			last = it;
+			break;
+		}
+	}
+	if (it == events.rend()
+			|| !approximatelyEqual(it->realArg1, lastMouveMoveEvent.realArg1)
+			|| !approximatelyEqual(it->realArg2, lastMouveMoveEvent.realArg2)) {
+		inputEventPayload->addEvent(lastMouveMoveEvent);
+	}
+}
+
 }
diff --git a/Swiften/ScreenSharing/IncomingScreenSharing.h b/Swiften/ScreenSharing/IncomingScreenSharing.h
index cc25a9f..c46a6f8 100644
--- a/Swiften/ScreenSharing/IncomingScreenSharing.h
+++ b/Swiften/ScreenSharing/IncomingScreenSharing.h
@@ -11,11 +11,17 @@
 #include <Swiften/Elements/RTPPayloadType.h>
 #include <Swiften/Base/boost_bsignals.h>
 
+#include <Swiften/Elements/InputEventPayload.h>
+
+
 namespace Swift {
 	class JingleContentPayload;
 	class VP8RTPParser;
 	class VideoDecoder;
 	class Image;
+	class TimerFactory;
+	class Timer;
+	class IQRouter;
 
 	class IncomingScreenSharing : public ScreenSharing {
 		public:
@@ -23,26 +29,33 @@ namespace Swift {
 
 		public:
 			IncomingScreenSharing(boost::shared_ptr<JingleSession> jingleSession, UDPSocketFactory* udpSocketFactory,
-								  boost::shared_ptr<JingleContentPayload> content);
+								  TimerFactory* timerFactory, IQRouter* iqRouter, boost::shared_ptr<JingleContentPayload> content);
 			virtual ~IncomingScreenSharing();
 
 			virtual void cancel();
-
 			void accept();
 
 			const JID& getSender() const;
 
+			void sendInputEvent(const InputEventPayload::Event& event);
+
 		public:
 			boost::signal<void (const Image&)> onNewImageReceived;
 
 		private:
 			JingleContentID getContentID() const;
-			void hangleNewImageDecoded(const Image& image);
+			void handleNewImageDecoded(const Image& image);
+			void handleEventSendingTimerTick();
+			void addLastMouseMoveIfDifferent();
 
 		private:
 			boost::shared_ptr<JingleContentPayload> initialContent;
 			RTPPayloadType payloadTypeUsed;
 			VP8RTPParser* parser;
 			VideoDecoder* decoder;
+			InputEventPayload::Event lastMouveMoveEvent;
+			InputEventPayload::ref inputEventPayload;
+			boost::shared_ptr<Timer> eventSendingTimer;
+			IQRouter* iqRouter;
 	};
 }
diff --git a/Swiften/ScreenSharing/IncomingScreenSharingManager.cpp b/Swiften/ScreenSharing/IncomingScreenSharingManager.cpp
index 321ab4c..8557f40 100644
--- a/Swiften/ScreenSharing/IncomingScreenSharingManager.cpp
+++ b/Swiften/ScreenSharing/IncomingScreenSharingManager.cpp
@@ -15,8 +15,9 @@
 
 namespace Swift {
 
-IncomingScreenSharingManager::IncomingScreenSharingManager(JingleSessionManager* jingleSessionManager, IQRouter* router, UDPSocketFactory* udpSocketFactory)
-	: jingleSessionManager(jingleSessionManager), router(router), udpSocketFactory(udpSocketFactory)
+IncomingScreenSharingManager::IncomingScreenSharingManager(JingleSessionManager* jingleSessionManager, IQRouter* iqRouter,
+														   UDPSocketFactory* udpSocketFactory, TimerFactory* timerFactory)
+	: jingleSessionManager(jingleSessionManager), iqRouter(iqRouter), udpSocketFactory(udpSocketFactory), timerFactory(timerFactory)
 {
 	jingleSessionManager->addIncomingSessionHandler(this);
 }
@@ -36,7 +37,7 @@ bool IncomingScreenSharingManager::handleIncomingJingleSession(Swift::JingleSess
 	// Check description
 	// Create IncomingScreenSharing
 
-	onIncomingScreenSharing(boost::make_shared<IncomingScreenSharing>(session, udpSocketFactory, content));
+	onIncomingScreenSharing(boost::make_shared<IncomingScreenSharing>(session, udpSocketFactory, timerFactory, iqRouter, content));
 
 	return true;
 }
diff --git a/Swiften/ScreenSharing/IncomingScreenSharingManager.h b/Swiften/ScreenSharing/IncomingScreenSharingManager.h
index 3563a87..586cb0b 100644
--- a/Swiften/ScreenSharing/IncomingScreenSharingManager.h
+++ b/Swiften/ScreenSharing/IncomingScreenSharingManager.h
@@ -13,10 +13,11 @@ namespace Swift {
 	class IQRouter;
 	class JingleSessionManager;
 	class UDPSocketFactory;
+	class TimerFactory;
 
 	class IncomingScreenSharingManager : public IncomingJingleSessionHandler {
 		public:
-			IncomingScreenSharingManager(JingleSessionManager* jingleSessionManager, IQRouter* router, UDPSocketFactory* udpSocketFactory);
+			IncomingScreenSharingManager(JingleSessionManager* jingleSessionManager, IQRouter* iqRouter, UDPSocketFactory* udpSocketFactory, TimerFactory* timerFactory);
 			virtual ~IncomingScreenSharingManager();
 
 		public:
@@ -27,7 +28,8 @@ namespace Swift {
 
 		private:
 			JingleSessionManager* jingleSessionManager;
-			IQRouter* router;
+			IQRouter* iqRouter;
 			UDPSocketFactory *udpSocketFactory;
+			TimerFactory* timerFactory;
 	};
 }
diff --git a/Swiften/ScreenSharing/InputEventResponder.cpp b/Swiften/ScreenSharing/InputEventResponder.cpp
new file mode 100644
index 0000000..ce9a541
--- /dev/null
+++ b/Swiften/ScreenSharing/InputEventResponder.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/ScreenSharing/InputEventResponder.h>
+#include <Swiften/ScreenSharing/ScreenSharingManager.h>
+
+namespace Swift {
+
+InputEventResponder::InputEventResponder(ScreenSharingManager* ssManager, IQRouter* router)
+	: SetResponder<InputEventPayload>(router), ssManager(ssManager)
+{
+}
+
+InputEventResponder::~InputEventResponder()
+{
+}
+
+bool InputEventResponder::handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr<InputEventPayload> payload)
+{
+	if (payload->getAction() == InputEventPayload::Notify) {
+		sendResponse(from, id, boost::shared_ptr<InputEventPayload>());
+		ssManager->handleInputEvent(from, payload);
+	}
+	return true;
+}
+
+}
diff --git a/Swiften/ScreenSharing/InputEventResponder.h b/Swiften/ScreenSharing/InputEventResponder.h
new file mode 100644
index 0000000..6d1acd3
--- /dev/null
+++ b/Swiften/ScreenSharing/InputEventResponder.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Queries/SetResponder.h>
+#include <Swiften/Elements/InputEventPayload.h>
+
+namespace Swift {
+	class IQRouter;
+	class JingleSessionManager;
+	class ScreenSharingManager;
+
+	class InputEventResponder : public SetResponder<InputEventPayload> {
+		public:
+			InputEventResponder(ScreenSharingManager* ssManager, IQRouter* router);
+			virtual ~InputEventResponder();
+
+		private:
+			virtual bool handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr<InputEventPayload> payload);
+
+		private:
+			ScreenSharingManager* ssManager;
+			IQRouter* router;
+	};
+}
diff --git a/Swiften/ScreenSharing/OutgoingScreenSharing.cpp b/Swiften/ScreenSharing/OutgoingScreenSharing.cpp
index 7cea50e..363b23c 100644
--- a/Swiften/ScreenSharing/OutgoingScreenSharing.cpp
+++ b/Swiften/ScreenSharing/OutgoingScreenSharing.cpp
@@ -23,9 +23,9 @@
 
 namespace Swift {
 
-OutgoingScreenSharing::OutgoingScreenSharing(boost::shared_ptr<JingleSession> session, UDPSocketFactory* udpSocketFactory, TimerFactory* timerFactory)
+OutgoingScreenSharing::OutgoingScreenSharing(boost::shared_ptr<JingleSession> session, UDPSocketFactory* udpSocketFactory, TimerFactory* timerFactory, const JID &toJID)
 	: ScreenSharing(session, udpSocketFactory),
-	  timerFactory(timerFactory), contentID(JingleContentID(idGenerator.generateID(), JingleContentPayload::InitiatorCreator)),
+	  timerFactory(timerFactory), recipient(toJID), contentID(JingleContentID(idGenerator.generateID(), JingleContentPayload::InitiatorCreator)),
 	  canceled(false), sessionAccepted(false), socketConnected(false), encoder(0), packetizer(0)
 {
 	jingleSession->onSessionAcceptReceived.connect(boost::bind(&OutgoingScreenSharing::handleSessionAcceptReceived, this, _1, _2, _3));
@@ -76,6 +76,11 @@ void OutgoingScreenSharing::addImage(const Image &image)
 	encoder->encodeImage(image);
 }
 
+const JID& OutgoingScreenSharing::getRecipient() const
+{
+	return recipient;
+}
+
 void OutgoingScreenSharing::handleSocketConnected()
 {
 	if (canceled)
diff --git a/Swiften/ScreenSharing/OutgoingScreenSharing.h b/Swiften/ScreenSharing/OutgoingScreenSharing.h
index cacc715..245d038 100644
--- a/Swiften/ScreenSharing/OutgoingScreenSharing.h
+++ b/Swiften/ScreenSharing/OutgoingScreenSharing.h
@@ -22,13 +22,14 @@ namespace Swift {
 	class VideoEncoder;
 	class VP8RTPPacketizer;
 	class Image;
+	class InputEventPayload;
 
 	class OutgoingScreenSharing : public ScreenSharing {
 		public:
 			typedef boost::shared_ptr<OutgoingScreenSharing> ref;
 
 		public:
-			OutgoingScreenSharing(boost::shared_ptr<JingleSession> jingleSession, UDPSocketFactory* udpSocketFactory, TimerFactory* timerFactory);
+			OutgoingScreenSharing(boost::shared_ptr<JingleSession> jingleSession, UDPSocketFactory* udpSocketFactory, TimerFactory* timerFactory, const JID& recipient);
 			virtual ~OutgoingScreenSharing();
 
 			virtual void cancel();
@@ -36,8 +37,11 @@ namespace Swift {
 			void start(unsigned int width, unsigned int height);
 			void addImage(const Image& image);
 
+			const JID& getRecipient() const;
+
 		public:
 			boost::signal<void ()> onReady;
+			boost::signal<void (boost::shared_ptr<InputEventPayload>)> onNewInputEvent;
 
 		private:
 			void handleSocketConnected();
@@ -48,6 +52,7 @@ namespace Swift {
 
 		private:
 			TimerFactory* timerFactory;
+			JID recipient;
 			JingleContentID contentID;
 			bool canceled;
 			bool sessionAccepted;
diff --git a/Swiften/ScreenSharing/OutgoingScreenSharingManager.cpp b/Swiften/ScreenSharing/OutgoingScreenSharingManager.cpp
index aaa2198..872fd19 100644
--- a/Swiften/ScreenSharing/OutgoingScreenSharingManager.cpp
+++ b/Swiften/ScreenSharing/OutgoingScreenSharingManager.cpp
@@ -19,13 +19,13 @@ OutgoingScreenSharingManager::OutgoingScreenSharingManager(JingleSessionManager*
 {
 }
 
-boost::shared_ptr<OutgoingScreenSharing> OutgoingScreenSharingManager::createOutgoingScreenSharing(const JID& from, const JID& to)
+boost::shared_ptr<OutgoingScreenSharing> OutgoingScreenSharingManager::createOutgoingScreenSharing(const JID& from, const JID& recipient)
 {
-	JingleSessionImpl::ref jingleSession = boost::make_shared<JingleSessionImpl>(from, to, idGenerator.generateID(), iqRouter);
+	JingleSessionImpl::ref jingleSession = boost::make_shared<JingleSessionImpl>(from, recipient, idGenerator.generateID(), iqRouter);
 	assert(jingleSession);
 	jsManager->registerOutgoingSession(from, jingleSession);
 
-	return boost::make_shared<OutgoingScreenSharing>(jingleSession, udpSocketFactory, timerFactory);
+	return boost::make_shared<OutgoingScreenSharing>(jingleSession, udpSocketFactory, timerFactory, recipient);
 }
 
 }
diff --git a/Swiften/ScreenSharing/OutgoingScreenSharingManager.h b/Swiften/ScreenSharing/OutgoingScreenSharingManager.h
index de21d5a..56888ba 100644
--- a/Swiften/ScreenSharing/OutgoingScreenSharingManager.h
+++ b/Swiften/ScreenSharing/OutgoingScreenSharingManager.h
@@ -22,7 +22,7 @@ namespace Swift {
 			OutgoingScreenSharingManager(JingleSessionManager* jingleSessionManager, IQRouter* router,
 										 UDPSocketFactory* udpSocketFactory, TimerFactory* timerFactory);
 
-			boost::shared_ptr<OutgoingScreenSharing> createOutgoingScreenSharing(const JID& from, const JID& to);
+			boost::shared_ptr<OutgoingScreenSharing> createOutgoingScreenSharing(const JID& from, const JID& recipient);
 
 		private:
 			IDGenerator idGenerator;
diff --git a/Swiften/ScreenSharing/SConscript b/Swiften/ScreenSharing/SConscript
index 1eeb443..aefe45b 100644
--- a/Swiften/ScreenSharing/SConscript
+++ b/Swiften/ScreenSharing/SConscript
@@ -9,6 +9,7 @@ sources = [
         "IncomingScreenSharingManager.cpp",
 		"ScreenSharingManagerImpl.cpp",
 		"VP8RTPParser.cpp",
+		"InputEventResponder.cpp",
     ]
 
 objects = swiften_env.SwiftenObject(sources)
diff --git a/Swiften/ScreenSharing/ScreenSharingManager.h b/Swiften/ScreenSharing/ScreenSharingManager.h
index a527402..6a97146 100644
--- a/Swiften/ScreenSharing/ScreenSharingManager.h
+++ b/Swiften/ScreenSharing/ScreenSharingManager.h
@@ -14,6 +14,7 @@ namespace Swift {
 	class IncomingScreenSharing;
 	class OutgoingScreenSharing;
 	class JID;
+	class InputEventPayload;
 
 	class ScreenSharingManager {
 		public:
@@ -21,6 +22,9 @@ namespace Swift {
 
 			virtual boost::shared_ptr<OutgoingScreenSharing> createOutgoingScreenSharing(const JID& to) = 0;
 
+			virtual void handleInputEvent(const JID& from, boost::shared_ptr<InputEventPayload> payload) = 0;
+
+		public:
 			boost::signal<void (boost::shared_ptr<IncomingScreenSharing>)> onIncomingScreenSharing;
 	};
 }
diff --git a/Swiften/ScreenSharing/ScreenSharingManagerImpl.cpp b/Swiften/ScreenSharing/ScreenSharingManagerImpl.cpp
index b0e337a..c20d2c7 100644
--- a/Swiften/ScreenSharing/ScreenSharingManagerImpl.cpp
+++ b/Swiften/ScreenSharing/ScreenSharingManagerImpl.cpp
@@ -12,22 +12,33 @@
 #include <Swiften/Presence/PresenceOracle.h>
 #include <Swiften/ScreenSharing/IncomingScreenSharingManager.h>
 #include <Swiften/ScreenSharing/OutgoingScreenSharingManager.h>
+#include <Swiften/ScreenSharing/OutgoingScreenSharing.h>
+#include <Swiften/ScreenSharing/InputEventResponder.h>
+
+#include <boost/foreach.hpp>
 
 namespace Swift {
 
 ScreenSharingManagerImpl::ScreenSharingManagerImpl(const JID& ownFullJID, JingleSessionManager *jingleSessionManager, IQRouter *iqRouter,
 												   UDPSocketFactory* udpSocketFactory, TimerFactory* timerFactory, PresenceOracle* presenceOrable,
 												   EntityCapsProvider* capsProvider)
-	: ownJID(ownFullJID)/*, jingleSM(jingleSessionManager), iqRouter(iqRouter), udpSocketFactory(udpSocketFactory), timerFactory(timerFactory)*/, capsProvider(capsProvider),  presenceOracle(presenceOrable)
+	: ownJID(ownFullJID), capsProvider(capsProvider),  presenceOracle(presenceOrable)
 {
-	incomingSSManager = new IncomingScreenSharingManager(jingleSessionManager, iqRouter, udpSocketFactory);
+	incomingSSManager = new IncomingScreenSharingManager(jingleSessionManager, iqRouter, udpSocketFactory, timerFactory);
 	outgoingSSManager = new OutgoingScreenSharingManager(jingleSessionManager, iqRouter, udpSocketFactory, timerFactory);
 
+	responder = new InputEventResponder(this, iqRouter);
+	responder->start();
+
 	incomingSSManager->onIncomingScreenSharing.connect(onIncomingScreenSharing);
 }
 
 ScreenSharingManagerImpl::~ScreenSharingManagerImpl()
 {
+	responder->stop();
+	delete responder;
+	delete incomingSSManager;
+	delete outgoingSSManager;
 }
 
 boost::shared_ptr<OutgoingScreenSharing> ScreenSharingManagerImpl::createOutgoingScreenSharing(const JID &to)
@@ -43,7 +54,18 @@ boost::shared_ptr<OutgoingScreenSharing> ScreenSharingManagerImpl::createOutgoin
 		}
 	}
 
-	return outgoingSSManager->createOutgoingScreenSharing(ownJID, recipient);
+	OutgoingScreenSharing::ref oss = outgoingSSManager->createOutgoingScreenSharing(ownJID, recipient);
+	outgoingSharings.push_back(oss);
+	return oss;
+}
+
+void ScreenSharingManagerImpl::handleInputEvent(const JID& from, boost::shared_ptr<InputEventPayload> payload)
+{
+	foreach (OutgoingScreenSharing::ref oss, outgoingSharings) {
+		if (oss->getRecipient() == from) {
+			oss->onNewInputEvent(payload);
+		}
+	}
 }
 
 boost::optional<JID> ScreenSharingManagerImpl::highestPriorityJIDSupportingScreenSharing(const JID& bareJID) {
diff --git a/Swiften/ScreenSharing/ScreenSharingManagerImpl.h b/Swiften/ScreenSharing/ScreenSharingManagerImpl.h
index 8e8ea78..8f73f87 100644
--- a/Swiften/ScreenSharing/ScreenSharingManagerImpl.h
+++ b/Swiften/ScreenSharing/ScreenSharingManagerImpl.h
@@ -18,6 +18,8 @@ namespace Swift {
 	class TimerFactory;
 	class PresenceOracle;
 	class EntityCapsProvider;
+	class InputEventResponder;
+	class InputEventPayload;
 
 	class ScreenSharingManagerImpl : public ScreenSharingManager {
 		public:
@@ -28,19 +30,18 @@ namespace Swift {
 
 			virtual boost::shared_ptr<OutgoingScreenSharing> createOutgoingScreenSharing(const JID& to);
 
+			virtual void handleInputEvent(const JID& from, boost::shared_ptr<InputEventPayload> payload);
+
 		private:
 			boost::optional<JID> highestPriorityJIDSupportingScreenSharing(const JID &bareJID);
 
 		private:
 			IncomingScreenSharingManager* incomingSSManager;
 			OutgoingScreenSharingManager* outgoingSSManager;
-
+			InputEventResponder* responder;
 			JID ownJID;
+			std::vector< boost::shared_ptr<OutgoingScreenSharing> > outgoingSharings;
 
-//			JingleSessionManager* jingleSM;
-//			IQRouter* iqRouter;
-//			BoostUDPSocketFactory* udpSocketFactory;
-//			TimerFactory* timerFactory;
 			EntityCapsProvider* capsProvider;
 			PresenceOracle* presenceOracle;
 	};
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
index 3a423d1..081284b 100644
--- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -64,6 +64,7 @@
 #include <Swiften/Serializer/PayloadSerializers/DeliveryReceiptSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/DeliveryReceiptRequestSerializer.h>
 #include <Swiften/Serializer/PayloadSerializers/JingleRTPDescriptionSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/InputEventSerializer.h>
 
 namespace Swift {
 
@@ -124,6 +125,7 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
 	serializers_.push_back(new DeliveryReceiptSerializer());
 	serializers_.push_back(new DeliveryReceiptRequestSerializer());
 	serializers_.push_back(new JingleRTPDescriptionSerializer());
+	serializers_.push_back(new InputEventSerializer());
 	
 	foreach(PayloadSerializer* serializer, serializers_) {
 		addSerializer(serializer);
diff --git a/Swiften/Serializer/PayloadSerializers/InputEventSerializer.cpp b/Swiften/Serializer/PayloadSerializers/InputEventSerializer.cpp
new file mode 100644
index 0000000..5692308
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/InputEventSerializer.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Serializer/PayloadSerializers/InputEventSerializer.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <Swiften/Base/foreach.h>
+#include <Swiften/Serializer/XML/XMLElement.h>
+
+namespace Swift {
+
+std::string InputEventSerializer::serializePayload(boost::shared_ptr<InputEventPayload> payload) const {
+	XMLElement inputevt("inputevt", "http://sip-comunicator.org/protocol/inputevt");
+	inputevt.setAttribute("action", actionToString(payload->getAction()));
+
+	XMLElement::ref remoteControlNode = boost::make_shared<XMLElement>("remote-control", "http://sip-communicator.org/protocol/inputevt");
+	foreach (InputEventPayload::Event event, payload->getEvents()) {
+		XMLElement::ref eventNode = boost::make_shared<XMLElement>(eventTypeToString(event.type));
+		switch (event.type) {
+			case InputEventPayload::Event::MouseMove:
+				eventNode->setAttribute("x", boost::lexical_cast<std::string>(event.realArg1));
+				eventNode->setAttribute("y", boost::lexical_cast<std::string>(event.realArg2));
+				break;
+			case InputEventPayload::Event::MouseWheel:
+				eventNode->setAttribute("notch", boost::lexical_cast<std::string>(event.integerArg));
+				break;
+			case InputEventPayload::Event::MousePress:
+			case InputEventPayload::Event::MouseRelease:
+				eventNode->setAttribute("btns", boost::lexical_cast<std::string>(event.integerArg));
+				break;
+			case InputEventPayload::Event::KeyType:
+				eventNode->setAttribute("keychar", boost::lexical_cast<std::string>(event.integerArg));
+				break;
+			case InputEventPayload::Event::KeyPress:
+			case InputEventPayload::Event::KeyRelease:
+				eventNode->setAttribute("keycode", boost::lexical_cast<std::string>(event.integerArg));
+				break;
+			default:
+				break;
+		}
+		remoteControlNode->addNode(eventNode);
+	}
+
+	inputevt.addNode(remoteControlNode);
+	return inputevt.serialize();
+}
+
+std::string InputEventSerializer::actionToString(InputEventPayload::Action action) {
+	switch (action) {
+		case InputEventPayload::Notify:
+			return "notify";
+		default:
+			std::cerr << "Serializing unknown action." << std::endl;
+	}
+	return "";
+}
+
+std::string InputEventSerializer::eventTypeToString(InputEventPayload::Event::EventType eventType) {
+	switch (eventType) {
+		case InputEventPayload::Event::MouseMove:
+			return "mouse-move";
+		case InputEventPayload::Event::MousePress:
+			return "mouse-press";
+		case InputEventPayload::Event::MouseRelease:
+			return "mouse-release";
+		case InputEventPayload::Event::MouseWheel:
+			return "mouse-wheel";
+		case InputEventPayload::Event::KeyPress:
+			return "key-press";
+		case InputEventPayload::Event::KeyRelease:
+			return "key-release";
+		case InputEventPayload::Event::KeyType:
+			return "key-type";
+		default:
+			std::cerr << "Serializing unknown event type." << std::endl;
+	}
+	return "";
+}
+
+}
diff --git a/Swiften/Serializer/PayloadSerializers/InputEventSerializer.h b/Swiften/Serializer/PayloadSerializers/InputEventSerializer.h
new file mode 100644
index 0000000..d7bdc11
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/InputEventSerializer.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2012 Yoann Blein
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/InputEventPayload.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+	class InputEventSerializer : public GenericPayloadSerializer<InputEventPayload> {
+		public:
+			virtual std::string serializePayload(boost::shared_ptr<InputEventPayload>) const;
+
+		private:
+			static std::string actionToString(InputEventPayload::Action action);
+			static std::string eventTypeToString(InputEventPayload::Event::EventType eventType);
+	};
+}
diff --git a/Swiften/Serializer/PayloadSerializers/JingleRTPDescriptionSerializer.cpp b/Swiften/Serializer/PayloadSerializers/JingleRTPDescriptionSerializer.cpp
index c0dfba1..34bc75c 100644
--- a/Swiften/Serializer/PayloadSerializers/JingleRTPDescriptionSerializer.cpp
+++ b/Swiften/Serializer/PayloadSerializers/JingleRTPDescriptionSerializer.cpp
@@ -19,9 +19,6 @@
 
 namespace Swift {
 
-JingleRTPDescriptionSerializer::JingleRTPDescriptionSerializer() {
-}
-
 std::string JingleRTPDescriptionSerializer::serializePayload(boost::shared_ptr<JingleRTPDescription> payload) const {
 	XMLElement description("description", "urn:xmpp:jingle:apps:rtp:1");
 	description.setAttribute("media", mediaTypeToString(payload->getMedia()));
@@ -48,7 +45,7 @@ std::string JingleRTPDescriptionSerializer::serializePayload(boost::shared_ptr<J
 	return description.serialize();
 }
 
-std::string JingleRTPDescriptionSerializer::mediaTypeToString(JingleRTPDescription::MediaType mediaType) const {
+std::string JingleRTPDescriptionSerializer::mediaTypeToString(JingleRTPDescription::MediaType mediaType) {
 	switch (mediaType) {
 		case JingleRTPDescription::Audio:
 			return "audio";
diff --git a/Swiften/Serializer/PayloadSerializers/JingleRTPDescriptionSerializer.h b/Swiften/Serializer/PayloadSerializers/JingleRTPDescriptionSerializer.h
index 3d23d85..1911b69 100644
--- a/Swiften/Serializer/PayloadSerializers/JingleRTPDescriptionSerializer.h
+++ b/Swiften/Serializer/PayloadSerializers/JingleRTPDescriptionSerializer.h
@@ -4,25 +4,17 @@
  * See Documentation/Licenses/BSD-simplified.txt for more information.
  */
 
-
 #pragma once
 
 #include <Swiften/Serializer/GenericPayloadSerializer.h>
 #include <Swiften/Elements/JingleRTPDescription.h>
 
-
-
 namespace Swift {
-	class PayloadSerializerCollection;
-	class XMLElement;
-
 	class JingleRTPDescriptionSerializer : public GenericPayloadSerializer<JingleRTPDescription> {
 		public:
-			JingleRTPDescriptionSerializer();
-
-			virtual std::string serializePayload(boost::shared_ptr<JingleRTPDescription>)  const;
+			virtual std::string serializePayload(boost::shared_ptr<JingleRTPDescription>) const;
 
 		private:
-			std::string mediaTypeToString(JingleRTPDescription::MediaType mediaType) const;
+			static std::string mediaTypeToString(JingleRTPDescription::MediaType mediaType);
 	};
 }
-- 
cgit v0.10.2-6-g49f6