From 955c4589ca254814ae7ec6ed911e0a62febc5da1 Mon Sep 17 00:00:00 2001 From: dknn 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 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(event->x()) / pixmap.width(); + float y = static_cast(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 +#include + +namespace Swift { + bool approximatelyEqual(float a, float b, float epsilon = std::numeric_limits::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::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::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::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 + +namespace Swift { + +InputEventPayload::InputEventPayload() + : action_(InputEventPayload::Notify) +{ +} + +void InputEventPayload::addEvent(const InputEventPayload::Event& event) +{ + events_.push_back(event); +} + +const std::vector& 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 + +#include +#include + +namespace Swift { + class InputEventPayload : public Payload { + public: + typedef boost::shared_ptr 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& getEvents() const; + + void setAction(Action action); + Action getAction() const; + + private: + Action action_; + std::vector 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 #include #include +#include using namespace boost; @@ -134,6 +135,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() { factories_.push_back(boost::make_shared(this)); factories_.push_back(boost::make_shared >("payload-type")); factories_.push_back(boost::make_shared >("transport", "urn:xmpp:jingle:transports:raw-udp:1")); + factories_.push_back(boost::make_shared >("inputevt", "http://sip-comunicator.org/protocol/inputevt")); foreach(shared_ptr 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 + +#include +#include + +namespace Swift { + +void InputEventParser::handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes) { + if (level == 0) { + // tag + InputEventPayload::ref payload = getPayloadInternal(); + payload->setAction(actionFromString(attributes.getAttributeValue("action").get_value_or(""))); + } else if (level == 1) { + // tag + } else if (level == 2) { + InputEventPayload::Event event(eventTypeFromString(element)); + switch (event.type) { + case InputEventPayload::Event::MouseMove: + try { + event.realArg1 = boost::lexical_cast(attributes.getAttributeValue("x").get_value_or("0")); + event.realArg2 = boost::lexical_cast(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(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 +#include + +namespace Swift { + class InputEventParser : public GenericPayloadParser { + 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 #include #include @@ -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 namespace Swift { - -class PayloadParserFactoryCollection; - -class JingleRTPDescriptionParser : public GenericPayloadParser { - 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 currentPayloadParser; - bool parsingBandwidth; - std::string bandwidthType; - std::string bandwidthValue; -}; - + class PayloadParserFactoryCollection; + + class JingleRTPDescriptionParser : public GenericPayloadParser { + 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 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 #include #include +#include +#include #include #include #include +#include +#include +#include #include namespace Swift { IncomingScreenSharing::IncomingScreenSharing(boost::shared_ptr session, UDPSocketFactory* udpSocketFactory, - boost::shared_ptr content) + TimerFactory* timerFactory, IQRouter* iqRouter, boost::shared_ptr content) : ScreenSharing(session, udpSocketFactory), - initialContent(content), parser(0), decoder(0) + initialContent(content), parser(0), decoder(0), lastMouveMoveEvent(InputEventPayload::Event::MouseMove), + inputEventPayload(boost::make_shared()), 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 > request + = boost::make_shared< GenericRequest >(IQ::Set, getSender(), inputEventPayload, iqRouter); + request->send(); + // Prepare for a new payload + inputEventPayload.reset(new InputEventPayload); +} + +void IncomingScreenSharing::addLastMouseMoveIfDifferent() +{ + const std::vector& events = inputEventPayload->getEvents(); + std::vector::const_reverse_iterator last; + std::vector::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 #include +#include + + 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, UDPSocketFactory* udpSocketFactory, - boost::shared_ptr content); + TimerFactory* timerFactory, IQRouter* iqRouter, boost::shared_ptr content); virtual ~IncomingScreenSharing(); virtual void cancel(); - void accept(); const JID& getSender() const; + void sendInputEvent(const InputEventPayload::Event& event); + public: boost::signal onNewImageReceived; private: JingleContentID getContentID() const; - void hangleNewImageDecoded(const Image& image); + void handleNewImageDecoded(const Image& image); + void handleEventSendingTimerTick(); + void addLastMouseMoveIfDifferent(); private: boost::shared_ptr initialContent; RTPPayloadType payloadTypeUsed; VP8RTPParser* parser; VideoDecoder* decoder; + InputEventPayload::Event lastMouveMoveEvent; + InputEventPayload::ref inputEventPayload; + boost::shared_ptr 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(session, udpSocketFactory, content)); + onIncomingScreenSharing(boost::make_shared(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 +#include + +namespace Swift { + +InputEventResponder::InputEventResponder(ScreenSharingManager* ssManager, IQRouter* router) + : SetResponder(router), ssManager(ssManager) +{ +} + +InputEventResponder::~InputEventResponder() +{ +} + +bool InputEventResponder::handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr payload) +{ + if (payload->getAction() == InputEventPayload::Notify) { + sendResponse(from, id, boost::shared_ptr()); + 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 +#include + +namespace Swift { + class IQRouter; + class JingleSessionManager; + class ScreenSharingManager; + + class InputEventResponder : public SetResponder { + public: + InputEventResponder(ScreenSharingManager* ssManager, IQRouter* router); + virtual ~InputEventResponder(); + + private: + virtual bool handleSetRequest(const JID& from, const JID&, const std::string& id, boost::shared_ptr 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 session, UDPSocketFactory* udpSocketFactory, TimerFactory* timerFactory) +OutgoingScreenSharing::OutgoingScreenSharing(boost::shared_ptr 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 ref; public: - OutgoingScreenSharing(boost::shared_ptr jingleSession, UDPSocketFactory* udpSocketFactory, TimerFactory* timerFactory); + OutgoingScreenSharing(boost::shared_ptr 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 onReady; + boost::signal)> 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 OutgoingScreenSharingManager::createOutgoingScreenSharing(const JID& from, const JID& to) +boost::shared_ptr OutgoingScreenSharingManager::createOutgoingScreenSharing(const JID& from, const JID& recipient) { - JingleSessionImpl::ref jingleSession = boost::make_shared(from, to, idGenerator.generateID(), iqRouter); + JingleSessionImpl::ref jingleSession = boost::make_shared(from, recipient, idGenerator.generateID(), iqRouter); assert(jingleSession); jsManager->registerOutgoingSession(from, jingleSession); - return boost::make_shared(jingleSession, udpSocketFactory, timerFactory); + return boost::make_shared(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 createOutgoingScreenSharing(const JID& from, const JID& to); + boost::shared_ptr 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 createOutgoingScreenSharing(const JID& to) = 0; + virtual void handleInputEvent(const JID& from, boost::shared_ptr payload) = 0; + + public: boost::signal)> 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 #include #include +#include +#include + +#include 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 ScreenSharingManagerImpl::createOutgoingScreenSharing(const JID &to) @@ -43,7 +54,18 @@ boost::shared_ptr 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 payload) +{ + foreach (OutgoingScreenSharing::ref oss, outgoingSharings) { + if (oss->getRecipient() == from) { + oss->onNewInputEvent(payload); + } + } } boost::optional 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 createOutgoingScreenSharing(const JID& to); + virtual void handleInputEvent(const JID& from, boost::shared_ptr payload); + private: boost::optional highestPriorityJIDSupportingScreenSharing(const JID &bareJID); private: IncomingScreenSharingManager* incomingSSManager; OutgoingScreenSharingManager* outgoingSSManager; - + InputEventResponder* responder; JID ownJID; + std::vector< boost::shared_ptr > 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 #include #include +#include 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 + +#include +#include + +#include +#include + +namespace Swift { + +std::string InputEventSerializer::serializePayload(boost::shared_ptr payload) const { + XMLElement inputevt("inputevt", "http://sip-comunicator.org/protocol/inputevt"); + inputevt.setAttribute("action", actionToString(payload->getAction())); + + XMLElement::ref remoteControlNode = boost::make_shared("remote-control", "http://sip-communicator.org/protocol/inputevt"); + foreach (InputEventPayload::Event event, payload->getEvents()) { + XMLElement::ref eventNode = boost::make_shared(eventTypeToString(event.type)); + switch (event.type) { + case InputEventPayload::Event::MouseMove: + eventNode->setAttribute("x", boost::lexical_cast(event.realArg1)); + eventNode->setAttribute("y", boost::lexical_cast(event.realArg2)); + break; + case InputEventPayload::Event::MouseWheel: + eventNode->setAttribute("notch", boost::lexical_cast(event.integerArg)); + break; + case InputEventPayload::Event::MousePress: + case InputEventPayload::Event::MouseRelease: + eventNode->setAttribute("btns", boost::lexical_cast(event.integerArg)); + break; + case InputEventPayload::Event::KeyType: + eventNode->setAttribute("keychar", boost::lexical_cast(event.integerArg)); + break; + case InputEventPayload::Event::KeyPress: + case InputEventPayload::Event::KeyRelease: + eventNode->setAttribute("keycode", boost::lexical_cast(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 +#include + +#include + +namespace Swift { + class InputEventSerializer : public GenericPayloadSerializer { + public: + virtual std::string serializePayload(boost::shared_ptr) 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 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 #include - - namespace Swift { - class PayloadSerializerCollection; - class XMLElement; - class JingleRTPDescriptionSerializer : public GenericPayloadSerializer { public: - JingleRTPDescriptionSerializer(); - - virtual std::string serializePayload(boost::shared_ptr) const; + virtual std::string serializePayload(boost::shared_ptr) const; private: - std::string mediaTypeToString(JingleRTPDescription::MediaType mediaType) const; + static std::string mediaTypeToString(JingleRTPDescription::MediaType mediaType); }; } -- cgit v0.10.2-6-g49f6