aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl/protocol/jabber
diff options
context:
space:
mode:
authorSebastien Vincent <seb@jitsi.org>2010-09-28 07:46:45 +0000
committerSebastien Vincent <seb@jitsi.org>2010-09-28 07:46:45 +0000
commit6a67589c86221faedcebd370824274013be64026 (patch)
treeb5a73d1deec90532057f70b801cafaa015a907e0 /src/net/java/sip/communicator/impl/protocol/jabber
parenta4ddb0361ebc3848e30eefbf0391c755adb3387c (diff)
downloadjitsi-6a67589c86221faedcebd370824274013be64026.zip
jitsi-6a67589c86221faedcebd370824274013be64026.tar.gz
jitsi-6a67589c86221faedcebd370824274013be64026.tar.bz2
Desktop sharing support for SIP and XMPP. Note that GUI is not ready yet to propose this feature to the users.
Diffstat (limited to 'src/net/java/sip/communicator/impl/protocol/jabber')
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java35
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java52
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java28
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingClientJabberImpl.java187
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingServerJabberImpl.java473
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java4
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java18
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtAction.java70
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQ.java142
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQProvider.java85
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtension.java194
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtensionProvider.java402
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/InputEvtPacketExtension.java37
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java9
-rwxr-xr-xsrc/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf1
15 files changed, 1732 insertions, 5 deletions
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
index 9088e91..eaf84af 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
@@ -36,6 +36,12 @@ public class CallJabberImpl extends MediaAwareCall<
private final OperationSetBasicTelephonyJabberImpl parentOpSet;
/**
+ * Indicates if the <tt>CallPeer</tt> will support </tt>inputevt</tt>
+ * extension (i.e. will be able to be remote-controlled).
+ */
+ private boolean localInputEvtAware = false;
+
+ /**
* Crates a CallJabberImpl instance belonging to <tt>sourceProvider</tt> and
* associated with the jingle session with the specified <tt>jingleSID</tt>.
* If this call corresponds to an incoming jingle session then the jingleSID
@@ -57,6 +63,26 @@ public class CallJabberImpl extends MediaAwareCall<
}
/**
+ * Enable or disable <tt>inputevt</tt> support (remote control).
+ *
+ * @param enable new state of inputevt support
+ */
+ public void setLocalInputEvtAware(boolean enable)
+ {
+ localInputEvtAware = enable;
+ }
+
+ /**
+ * Returns if the call support <tt>inputevt</tt> (remote control).
+ *
+ * @return true if the call support <tt>inputevt</tt>, false otherwise
+ */
+ public boolean getLocalInputEvtAware()
+ {
+ return localInputEvtAware;
+ }
+
+ /**
* Creates a new call peer and sends a RINGING response.
*
* @param jingleIQ the {@link JingleIQ} that created the session.
@@ -132,6 +158,8 @@ public class CallJabberImpl extends MediaAwareCall<
/* enable video if it is a videocall */
callPeer.getMediaHandler().setLocalVideoTransmissionEnabled(
localVideoAllowed);
+ /* enable remote-control if it is a desktop sharing session */
+ callPeer.getMediaHandler().setLocalInputEvtAware(localInputEvtAware);
//set call state to connecting so that the user interface would start
//playing the tones. we do that here because we may be harvesting
@@ -151,8 +179,13 @@ public class CallJabberImpl extends MediaAwareCall<
* @throws OperationFailedException if problem occurred during message
* generation or network problem
*/
- public void modifyVideoContent(boolean allowed) throws OperationFailedException
+ public void modifyVideoContent(boolean allowed)
+ throws OperationFailedException
{
+ if(logger.isInfoEnabled())
+ logger.info(allowed ? "Start local video streaming" :
+ "Stop local video streaming");
+
for(CallPeerJabberImpl peer : getCallPeersVector())
{
peer.sendModifyVideoContent(allowed);
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
index e03c4ba..1ab2ae3 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
@@ -61,6 +61,12 @@ public class CallPeerMediaHandlerJabberImpl
private boolean remotelyOnHold = false;
/**
+ * Indicates if the <tt>CallPeer</tt> will support </tt>inputevt</tt>
+ * extension (i.e. will be able to be remote-controlled).
+ */
+ private boolean localInputEvtAware = false;
+
+ /**
* Creates a new handler that will be managing media streams for
* <tt>peer</tt>.
*
@@ -97,6 +103,16 @@ public class CallPeerMediaHandlerJabberImpl
}
/**
+ * Enable or disable <tt>inputevt</tt> support (remote-control).
+ *
+ * @param enable new state of inputevt support
+ */
+ public void setLocalInputEvtAware(boolean enable)
+ {
+ localInputEvtAware = enable;
+ }
+
+ /**
* Get the remote content of a specific content type (like audio or video).
*
* @param contentType content type name
@@ -300,6 +316,14 @@ public class CallPeerMediaHandlerJabberImpl
}
}
+ // got an content which have inputevt, it means that peer requests
+ // a desktop sharing session so tell it we support inputevt
+ if(content.getChildExtensionsOfType(
+ InputEvtPacketExtension.class) != null)
+ {
+ ourContent.addChildExtension(new InputEvtPacketExtension());
+ }
+
answerContentList.add(ourContent);
localContentMap.put(content.getName(), ourContent);
@@ -395,6 +419,22 @@ public class CallPeerMediaHandlerJabberImpl
// create the corresponding stream...
initStream(ourContent.getName(), connector, dev, format, target,
direction, rtpExtensions);
+
+ // if remote peer requires inputevt, notify UI to capture mouse
+ // and keyboard events
+ if(ourContent.getChildExtensionsOfType(
+ InputEvtPacketExtension.class) != null)
+ {
+ OperationSetDesktopSharingClientJabberImpl client =
+ (OperationSetDesktopSharingClientJabberImpl)
+ this.getPeer().getProtocolProvider().getOperationSet(
+ OperationSetDesktopSharingClient.class);
+
+ if(client != null)
+ {
+ client.fireRemoteControlGranted();
+ }
+ }
}
return sessAccept;
}
@@ -562,6 +602,18 @@ public class CallPeerMediaHandlerJabberImpl
}
}
+ /* we request a desktop sharing session so add the inputevt
+ * extension in the "video" content
+ */
+ RtpDescriptionPacketExtension description
+ = JingleUtils.getRtpDescription(content);
+ if(description.getMedia().equals(
+ MediaType.VIDEO.toString()) && localInputEvtAware)
+ {
+ content.addChildExtension(
+ new InputEvtPacketExtension());
+ }
+
mediaDescs.add(content);
}
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java b/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java
index 7e7f723..94181b6 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/JabberActivator.java
@@ -10,6 +10,7 @@ import java.util.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.gui.*;
+import net.java.sip.communicator.service.hid.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.netaddr.*;
import net.java.sip.communicator.service.protocol.*;
@@ -58,7 +59,8 @@ public class JabberActivator
/**
* The Jabber protocol provider factory.
*/
- private static ProtocolProviderFactoryJabberImpl jabberProviderFactory = null;
+ private static ProtocolProviderFactoryJabberImpl
+ jabberProviderFactory = null;
/**
* The <tt>UriHandler</tt> implementation that we use to handle "xmpp:" URIs
@@ -77,6 +79,11 @@ public class JabberActivator
private static ResourceManagementService resourcesService = null;
/**
+ * A reference to the currently valid <tt>HIDService</tt> instance.
+ */
+ private static HIDService hidService = null;
+
+ /**
* Called when this bundle is started so the Framework can perform the
* bundle-specific activities necessary to start this bundle.
*
@@ -274,4 +281,23 @@ public class JabberActivator
}
return networkAddressManagerService;
}
+
+ /**
+ * Returns a reference to <tt>HIDService</tt> implementation currently
+ * registered in the bundle context or null if no such implementation was
+ * found
+ *
+ * @return a currently valid implementation of the <tt>HIDService</tt>
+ */
+ public static HIDService getHIDService()
+ {
+ if(hidService == null)
+ {
+ ServiceReference hidReference =
+ bundleContext.getServiceReference(
+ HIDService.class.getName());
+ hidService = (HIDService)bundleContext.getService(hidReference);
+ }
+ return hidService;
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingClientJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingClientJabberImpl.java
new file mode 100644
index 0000000..190ddc7
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingClientJabberImpl.java
@@ -0,0 +1,187 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.List; // disambiguation
+
+import org.jivesoftware.smack.packet.*;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implements all desktop sharing client-side related functions for Jabber
+ * protocol.
+ *
+ * @author Sebastien Vincent
+ */
+public class OperationSetDesktopSharingClientJabberImpl
+ implements OperationSetDesktopSharingClient
+{
+ /**
+ * Our class logger.
+ */
+ private static final Logger logger =
+ Logger.getLogger(OperationSetDesktopSharingClientJabberImpl.class);
+
+ /**
+ * The Jabber <tt>ProtocolProviderService</tt> implementation which created
+ * this instance and for which telephony conferencing services are being
+ * provided by this instance.
+ */
+ private final ProtocolProviderServiceJabberImpl parentProvider;
+
+ /**
+ * List of listeners to be notified when a change occurred in remote control
+ * access.
+ */
+ private List<RemoteControlListener> listeners =
+ new ArrayList<RemoteControlListener>();
+
+ /**
+ * Initializes a new <tt>OperationSetDesktopSharingClientJabberImpl</tt>.
+ *
+ * @param parentProvider the Jabber <tt>ProtocolProviderService</tt>
+ * implementation which has requested the creation of the new instance and
+ * for which the new instance is to provide desktop sharing.
+ */
+ public OperationSetDesktopSharingClientJabberImpl(
+ ProtocolProviderServiceJabberImpl parentProvider)
+ {
+ this.parentProvider = parentProvider;
+ }
+
+ /**
+ * Fire a <tt>RemoteControlGrantedEvent</tt> to all registered listeners.
+ */
+ public void fireRemoteControlGranted()
+ {
+ RemoteControlGrantedEvent event = new RemoteControlGrantedEvent(this);
+
+ for (RemoteControlListener l : listeners)
+ {
+ l.remoteControlGranted(event);
+ }
+ }
+
+ /**
+ * Fire a <tt>RemoteControlGrantedEvent</tt> to all registered listeners.
+ */
+ public void fireRemoteControlRevoked()
+ {
+ RemoteControlRevokedEvent event = new RemoteControlRevokedEvent(this);
+
+ for (RemoteControlListener l : listeners)
+ {
+ l.remoteControlRevoked(event);
+ }
+ }
+
+ /**
+ * Add a <tt>RemoteControlListener</tt> to be notified when remote peer
+ * accept to give us full control.
+ *
+ * @param listener <tt>RemoteControlListener</tt> to add
+ */
+ public void addRemoteControlListener(RemoteControlListener listener)
+ {
+ if (logger.isInfoEnabled())
+ logger.info("Enable remote control");
+
+ if (!listeners.contains(listener))
+ {
+ listeners.add(listener);
+ }
+ }
+
+ /**
+ * Remove a <tt>RemoteControlListener</tt> to be notified when remote peer
+ * accept/revoke to give us full control.
+ *
+ * @param listener <tt>RemoteControlListener</tt> to remove
+ */
+ public void removeRemoteControlListener(RemoteControlListener listener)
+ {
+ if (logger.isInfoEnabled())
+ logger.info("Disable remote control");
+
+ if (listeners.contains(listener))
+ {
+ listeners.remove(listener);
+ }
+ }
+
+ /**
+ * Send a keyboard notification.
+ *
+ * @param callPeer <tt>CallPeer</tt> that will be notified
+ * @param event <tt>KeyEvent</tt> received and that will be send to remote
+ * peer
+ */
+ public void sendKeyboardEvent(CallPeer callPeer, KeyEvent event)
+ {
+ RemoteControlExtension payload = new RemoteControlExtension(event);
+ InputEvtIQ inputIQ = new InputEvtIQ();
+
+ inputIQ.setAction(InputEvtAction.NOTIFY);
+ inputIQ.setType(IQ.Type.SET);
+ inputIQ.setFrom(parentProvider.getOurJID());
+ inputIQ.setTo(callPeer.getAddress());
+ inputIQ.addRemoteControl(payload);
+ parentProvider.getConnection().sendPacket(inputIQ);
+ }
+
+ /**
+ * Send a mouse notification.
+ *
+ * @param callPeer <tt>CallPeer</tt> that will be notified
+ * @param event <tt>MouseEvent</tt> received and that will be send to remote
+ * peer
+ */
+ public void sendMouseEvent(CallPeer callPeer, MouseEvent event)
+ {
+ RemoteControlExtension payload = new RemoteControlExtension(event);
+ InputEvtIQ inputIQ = new InputEvtIQ();
+
+ inputIQ.setAction(InputEvtAction.NOTIFY);
+ inputIQ.setType(IQ.Type.SET);
+ inputIQ.setFrom(parentProvider.getOurJID());
+ inputIQ.setTo(callPeer.getAddress());
+ inputIQ.addRemoteControl(payload);
+ parentProvider.getConnection().sendPacket(inputIQ);
+ }
+
+ /**
+ * Send a mouse notification for specific "moved" <tt>MouseEvent</tt>. As
+ * controller computer could have smaller desktop that controlled ones, we
+ * should take care to send the percentage of point x and point y.
+ *
+ * @param callPeer <tt>CallPeer</tt> that will be notified
+ * @param event <tt>MouseEvent</tt> received and that will be send to remote
+ * peer
+ * @param videoPanelSize size of the panel that contains video
+ */
+ public void sendMouseEvent(CallPeer callPeer, MouseEvent event,
+ Dimension videoPanelSize)
+ {
+ RemoteControlExtension payload = new RemoteControlExtension(event,
+ videoPanelSize);
+ InputEvtIQ inputIQ = new InputEvtIQ();
+
+ inputIQ.setAction(InputEvtAction.NOTIFY);
+ inputIQ.setType(IQ.Type.SET);
+ inputIQ.setFrom(parentProvider.getOurJID());
+ inputIQ.setTo(callPeer.getAddress());
+ inputIQ.addRemoteControl(payload);
+ parentProvider.getConnection().sendPacket(inputIQ);
+ }
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingServerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingServerJabberImpl.java
new file mode 100644
index 0000000..093c195
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetDesktopSharingServerJabberImpl.java
@@ -0,0 +1,473 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.util.*;
+import java.util.List; // disambiguation
+
+import java.awt.*;
+import java.awt.event.*;
+
+import org.jivesoftware.smack.*;
+import org.jivesoftware.smack.filter.*;
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smackx.packet.*;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt.*;
+import net.java.sip.communicator.service.hid.*;
+import net.java.sip.communicator.service.neomedia.*;
+import net.java.sip.communicator.service.neomedia.format.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.service.protocol.event.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implements all desktop sharing server-side related functions for Jabber
+ * protocol.
+ *
+ * @author Sebastien Vincent
+ */
+public class OperationSetDesktopSharingServerJabberImpl
+ extends OperationSetDesktopStreamingJabberImpl
+ implements OperationSetDesktopSharingServer,
+ RegistrationStateChangeListener,
+ PacketListener,
+ PacketFilter
+{
+ /**
+ * Our class logger.
+ */
+ private static final Logger logger = Logger
+ .getLogger(OperationSetDesktopSharingServerJabberImpl.class);
+
+ /**
+ * The <tt>CallPeerListener</tt> which listens to modifications in the
+ * properties/state of <tt>CallPeer</tt>.
+ */
+ private final CallPeerListener callPeerListener = new CallPeerAdapter()
+ {
+ /**
+ * Indicates that a change has occurred in the status of the source
+ * <tt>CallPeer</tt>.
+ *
+ * @param evt the <tt>CallPeerChangeEvent</tt> instance containing the
+ * source event as well as its previous and its new status
+ */
+ @Override
+ public void peerStateChanged(CallPeerChangeEvent evt)
+ {
+ CallPeer peer = evt.getSourceCallPeer();
+ CallPeerState state = peer.getState();
+
+ if (remoteControlEnabled && state != null &&
+ (state.equals(CallPeerState.DISCONNECTED) ||
+ state.equals(CallPeerState.FAILED)))
+ {
+ disableRemoteControl(evt.getSourceCallPeer());
+ }
+ else if(state != null && state.equals(CallPeerState.CONNECTED))
+ {
+ /* connected peer */
+ enableRemoteControl(evt.getSourceCallPeer());
+ }
+ }
+ };
+
+ /**
+ * If the remote control is authorized and thus enabled.
+ */
+ private boolean remoteControlEnabled = false;
+
+ /**
+ * HID service that will regenerates keyboard and mouse events received in
+ * Jabber messages.
+ */
+ private HIDService hidService = null;
+
+ /**
+ * List of callPeers for the desktop sharing session.
+ */
+ private List<String> callPeers = new ArrayList<String>();
+
+ /**
+ * Video panel size.
+ */
+ private Dimension size = null;
+
+ /**
+ * Initializes a new <tt>OperationSetDesktopSharingJabberImpl</tt> instance
+ * which builds upon the telephony-related functionality of a specific
+ * <tt>OperationSetBasicTelephonyJabberImpl</tt>.
+ *
+ * @param basicTelephony the <tt>OperationSetBasicTelephonyJabberImpl</tt>
+ * the new extension should build upon
+ */
+ public OperationSetDesktopSharingServerJabberImpl(
+ OperationSetBasicTelephonyJabberImpl basicTelephony)
+ {
+ super(basicTelephony);
+
+ parentProvider.addRegistrationStateChangeListener(this);
+ hidService = JabberActivator.getHIDService();
+ }
+
+ /**
+ * Create a new video call and invite the specified CallPeer to it.
+ *
+ * @param uri the address of the callee that we should invite to a new
+ * call.
+ * @return CallPeer the CallPeer that will represented by the
+ * specified uri. All following state change events will be delivered
+ * through that call peer. The Call that this peer is a member
+ * of could be retrieved from the CallParticipatn instance with the use
+ * of the corresponding method.
+ * @throws OperationFailedException with the corresponding code if we fail
+ * to create the video call.
+ */
+ @Override
+ public Call createVideoCall(String uri)
+ throws OperationFailedException
+ {
+ CallJabberImpl call = (CallJabberImpl)super.createVideoCall(uri);
+ CallPeerJabberImpl callPeer = call.getCallPeers().next();
+ callPeer.addCallPeerListener(callPeerListener);
+
+ size = (((VideoMediaFormat)call.getDefaultDevice(
+ MediaType.VIDEO).getFormat()).getSize());
+ return call;
+ }
+
+ /**
+ * Create a new video call and invite the specified CallPeer to it.
+ *
+ * @param callee the address of the callee that we should invite to a new
+ * call.
+ * @return CallPeer the CallPeer that will represented by the
+ * specified uri. All following state change events will be delivered
+ * through that call peer. The Call that this peer is a member
+ * of could be retrieved from the CallParticipatn instance with the use
+ * of the corresponding method.
+ * @throws OperationFailedException with the corresponding code if we fail
+ * to create the video call.
+ */
+ @Override
+ public Call createVideoCall(Contact callee) throws OperationFailedException
+ {
+ CallJabberImpl call = (CallJabberImpl)super.createVideoCall(callee);
+ CallPeerJabberImpl callPeer = call.getCallPeers().next();
+ callPeer.addCallPeerListener(callPeerListener);
+
+ size = (((VideoMediaFormat)call.getDefaultDevice(
+ MediaType.VIDEO).getFormat()).getSize());
+ return call;
+ }
+
+ /**
+ * Check if the remote part supports Jingle video.
+ *
+ * @param calleeAddress Contact address
+ * @return true if contact support Jingle video, false otherwise
+ *
+ * @throws OperationFailedException with the corresponding code if we fail
+ * to create the video call.
+ */
+ protected Call createOutgoingVideoCall(String calleeAddress)
+ throws OperationFailedException
+ {
+ boolean supported = false;
+ String fullCalleeURI = null;
+
+ if (calleeAddress.indexOf('/') > 0)
+ {
+ fullCalleeURI = calleeAddress;
+ }
+ else
+ {
+ fullCalleeURI = parentProvider.getConnection()
+ .getRoster().getPresence(calleeAddress).getFrom();
+ }
+
+ if (logger.isInfoEnabled())
+ logger.info("creating outgoing desktop sharing call...");
+
+ DiscoverInfo di = null;
+ try
+ {
+ // check if the remote client supports inputevt (remote control)
+ di = parentProvider.getDiscoveryManager()
+ .discoverInfo(fullCalleeURI);
+
+ if (di.containsFeature(InputEvtIQ.NAMESPACE))
+ {
+ if (logger.isInfoEnabled())
+ logger.info(fullCalleeURI + ": remote-control supported");
+
+ supported = true;
+ }
+ else
+ {
+ if (logger.isInfoEnabled())
+ logger.info(fullCalleeURI +
+ ": remote-control not supported!");
+
+ /* XXX fail or not ? */
+ /*
+ throw new OperationFailedException(
+ "Failed to create a true desktop sharing.\n"
+ + fullCalleeURI + " does not support inputevt",
+ OperationFailedException.INTERNAL_ERROR);
+ */
+ }
+ }
+ catch (XMPPException ex)
+ {
+ logger.warn("could not retrieve info for " + fullCalleeURI, ex);
+ }
+
+ if (parentProvider.getConnection() == null)
+ {
+ throw new OperationFailedException(
+ "Failed to create OutgoingJingleSession.\n"
+ + "we don't have a valid XMPPConnection."
+ , OperationFailedException.INTERNAL_ERROR);
+ }
+
+ CallJabberImpl call = new CallJabberImpl(basicTelephony);
+ /* enable video */
+ call.setLocalVideoAllowed(true, getMediaUseCase());
+ /* enable remote-control */
+ call.setLocalInputEvtAware(supported);
+
+ return basicTelephony.createOutgoingCall(call, calleeAddress);
+ }
+
+ /**
+ * Enable desktop remote control. Local desktop can now regenerates keyboard
+ * and mouse events received from peer.
+ *
+ * @param callPeer call peer that will take control on local computer
+ */
+ public void enableRemoteControl(CallPeer callPeer)
+ {
+ if(logger.isInfoEnabled())
+ logger.info("Enable remote control");
+
+ CallJabberImpl call = (CallJabberImpl)callPeer.getCall();
+ if(call.getLocalInputEvtAware())
+ {
+ remoteControlEnabled = true;
+
+ if(!callPeers.contains(callPeer.getAddress()))
+ {
+ callPeers.add(callPeer.getAddress());
+ }
+ }
+ }
+
+ /**
+ * Disable desktop remote control. Local desktop stop regenerates keyboard
+ * and mouse events received from peer.
+ *
+ * @param callPeer call peer that will stop controlling on local computer
+ */
+ public void disableRemoteControl(CallPeer callPeer)
+ {
+ if(logger.isInfoEnabled())
+ logger.info("Disable remote control");
+
+ remoteControlEnabled = false;
+
+ if(callPeers.contains(callPeer.getAddress()))
+ {
+ callPeers.remove(callPeer.getAddress());
+ }
+ }
+
+ /**
+ * Implementation of method <tt>registrationStateChange</tt> from
+ * interface RegistrationStateChangeListener for setting up (or down)
+ * our <tt>InputEvtManager</tt> when an <tt>XMPPConnection</tt> is available
+ *
+ * @param evt the event received
+ */
+ public void registrationStateChanged(RegistrationStateChangeEvent evt)
+ {
+ if ((evt.getNewState() == RegistrationState.REGISTERING))
+ {
+ /* listen to specific inputevt IQ */
+ parentProvider.getConnection().addPacketListener(this, this);
+ }
+ }
+
+ /**
+ * Handles incoming inputevt packets and passes them to the corresponding
+ * method based on their action.
+ *
+ * @param packet the packet to process.
+ */
+ public void processPacket(Packet packet)
+ {
+ //this is not supposed to happen because of the filter ... but still
+ if (!(packet instanceof InputEvtIQ))
+ return;
+
+ InputEvtIQ inputIQ = (InputEvtIQ)packet;
+
+ if(inputIQ.getAction() != InputEvtAction.NOTIFY)
+ {
+ return;
+ }
+
+ /* do not waste time to parse packet if remote control is not enabled */
+ if(!remoteControlEnabled)
+ {
+ return;
+ }
+
+ //first ack all "set" requests.
+ if(inputIQ.getType() == IQ.Type.SET)
+ {
+ IQ ack = IQ.createResultIQ(inputIQ);
+ parentProvider.getConnection().sendPacket(ack);
+ }
+
+ if(!callPeers.contains(inputIQ.getFrom()))
+ {
+ return;
+ }
+
+ for(RemoteControlExtension p : inputIQ.getRemoteControls())
+ {
+ ComponentEvent evt = p.getEvent();
+ processComponentEvent(evt);
+ }
+ }
+
+ /**
+ * Tests whether or not the specified packet should be handled by this
+ * operation set. This method is called by smack prior to packet delivery
+ * and it would only accept <tt>InputEvtIQ</tt>s.
+ *
+ * @param packet the packet to test.
+ * @return true if and only if <tt>packet</tt> passes the filter.
+ */
+ public boolean accept(Packet packet)
+ {
+ //we only handle InputEvtIQ-s
+ if(!(packet instanceof InputEvtIQ))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Process an <tt>ComponentEvent</tt> received from remote peer.
+ *
+ * @param event <tt>ComponentEvent</tt> that will be regenerated on local
+ * computer
+ */
+ public void processComponentEvent(ComponentEvent event)
+ {
+ if(event == null)
+ {
+ return;
+ }
+
+ if(event instanceof KeyEvent)
+ {
+ processKeyboardEvent((KeyEvent)event);
+ }
+ else if(event instanceof MouseEvent)
+ {
+ processMouseEvent((MouseEvent)event);
+ }
+ }
+
+ /**
+ * Process keyboard notification received from remote peer.
+ *
+ * @param event <tt>KeyboardEvent</tt> that will be regenerated on local
+ * computer
+ */
+ public void processKeyboardEvent(KeyEvent event)
+ {
+ /* ignore command if remote control is not enabled otherwise regenerates
+ * event on the computer
+ */
+ if (hidService != null)
+ {
+ int keycode = 0;
+
+ /* process immediately a "key-typed" event via press/release */
+ if(event.getKeyChar() != 0 && event.getID() == KeyEvent.KEY_TYPED)
+ {
+ hidService.keyPress(event.getKeyChar());
+ hidService.keyRelease(event.getKeyChar());
+ return;
+ }
+
+ keycode = event.getKeyCode();
+
+ if(keycode == 0)
+ {
+ return;
+ }
+
+ switch(event.getID())
+ {
+ case KeyEvent.KEY_PRESSED:
+ hidService.keyPress(keycode);
+ break;
+ case KeyEvent.KEY_RELEASED:
+ hidService.keyRelease(keycode);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Process mouse notification received from remote peer.
+ *
+ * @param event <tt>MouseEvent</tt> that will be regenerated on local
+ * computer
+ */
+ public void processMouseEvent(MouseEvent event)
+ {
+ /* ignore command if remote control is not enabled otherwise regenerates
+ * event on the computer
+ */
+ if (hidService != null)
+ {
+ switch(event.getID())
+ {
+ case MouseEvent.MOUSE_PRESSED:
+ hidService.mousePress(event.getModifiers());
+ break;
+ case MouseEvent.MOUSE_RELEASED:
+ hidService.mouseRelease(event.getModifiers());
+ break;
+ case MouseEvent.MOUSE_MOVED:
+ /* x and y position are sent in percentage but we multiply
+ * by 1000 in depacketizer because we cannot passed the size
+ * to the Provider
+ */
+ int x = ((event.getX() * size.width) / 1000);
+ int y = ((event.getY() * size.height) / 1000);
+ hidService.mouseMove(x, y);
+ break;
+ case MouseEvent.MOUSE_WHEEL:
+ MouseWheelEvent evt = (MouseWheelEvent)event;
+ hidService.mouseWheel(evt.getWheelRotation());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java
index 0b6fc00..cfb7c0e 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/OperationSetVideoTelephonyJabberImpl.java
@@ -110,10 +110,10 @@ public class OperationSetVideoTelephonyJabberImpl
* @param calleeAddress Contact address
* @return true if contact support Jingle video, false otherwise
*
- * @exception OperationFailedException with the corresponding code if we fail
+ * @throws OperationFailedException with the corresponding code if we fail
* to create the video call.
*/
- private Call createOutgoingVideoCall(String calleeAddress)
+ protected Call createOutgoingVideoCall(String calleeAddress)
throws OperationFailedException
{
if (logger.isInfoEnabled())
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
index a606c6a..a8f29d4 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
@@ -18,6 +18,7 @@ import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.jabberconstants.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
+import net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt.*;
import net.java.sip.communicator.impl.protocol.jabber.sasl.*;
import net.java.sip.communicator.service.gui.*;
@@ -1078,6 +1079,11 @@ public class ProtocolProviderServiceJabberImpl
JingleIQ.NAMESPACE,
new JingleIQProvider());
+ // register our input event provider
+ providerManager.addIQProvider(InputEvtIQ.ELEMENT_NAME,
+ InputEvtIQ.NAMESPACE,
+ new InputEvtIQProvider());
+
//initialize the telephony operation set
//until we actually finish jingle, we'll have a clumsy way of
//enabling it through a system property.
@@ -1104,6 +1110,15 @@ public class ProtocolProviderServiceJabberImpl
OperationSetDesktopStreaming.class,
new OperationSetDesktopStreamingJabberImpl(basicTelephony));
+ // initialize desktop sharing OperationSets
+ addSupportedOperationSet(
+ OperationSetDesktopSharingServer.class,
+ new OperationSetDesktopSharingServerJabberImpl(
+ basicTelephony));
+ addSupportedOperationSet(
+ OperationSetDesktopSharingClient.class,
+ new OperationSetDesktopSharingClientJabberImpl(this));
+
// Add Jingle features to supported features.
supportedFeatures.add(URN_XMPP_JINGLE);
supportedFeatures.add(URN_XMPP_JINGLE_RTP);
@@ -1113,6 +1128,9 @@ public class ProtocolProviderServiceJabberImpl
supportedFeatures.add(URN_XMPP_JINGLE_RTP_AUDIO);
supportedFeatures.add(URN_XMPP_JINGLE_RTP_VIDEO);
supportedFeatures.add(URN_XMPP_JINGLE_RTP_ZRTP);
+
+ /* add extension to support remote control */
+ supportedFeatures.add(InputEvtIQ.NAMESPACE);
}
// OperationSetContactCapabilities
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtAction.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtAction.java
new file mode 100644
index 0000000..e15f321
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtAction.java
@@ -0,0 +1,70 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt;
+
+/**
+ * Enumeration about the possible actions for an InputEvt IQ.
+ *
+ * @author Sebastien Vincent
+ */
+public enum InputEvtAction
+{
+ /**
+ * The <tt>notify</tt> action.
+ */
+ NOTIFY("notify");
+
+ /**
+ * The name of this direction.
+ */
+ private final String actionName;
+
+ /**
+ * Creates a <tt>InputEvtAction</tt> instance with the specified name.
+ *
+ * @param actionName the name of the <tt>InputEvtAction</tt> we'd like
+ * to create.
+ */
+ private InputEvtAction(String actionName)
+ {
+ this.actionName = actionName;
+ }
+
+ /**
+ * Returns the name of this <tt>InputEvtAction</tt>. The name returned by
+ * this method is meant for use directly in the XMPP XML string.
+ *
+ * @return Returns the name of this <tt>InputEvtAction</tt>.
+ */
+ @Override
+ public String toString()
+ {
+ return actionName;
+ }
+
+ /**
+ * Returns a <tt>InputEvtAction</tt> value corresponding to the specified
+ * <tt>inputActionStr</tt>.
+ *
+ * @param inputActionStr the action <tt>String</tt> that we'd like to
+ * parse.
+ * @return a <tt>InputEvtAction</tt> value corresponding to the specified
+ * <tt>inputActionStr</tt>.
+ * @throws IllegalArgumentException in case <tt>inputActionStr</tt> is
+ * not valid
+ */
+ public static InputEvtAction parseString(String inputActionStr)
+ throws IllegalArgumentException
+ {
+ for (InputEvtAction value : values())
+ if (value.toString().equals(inputActionStr))
+ return value;
+
+ throw new IllegalArgumentException(
+ inputActionStr + " is not a valid Input action");
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQ.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQ.java
new file mode 100644
index 0000000..c506401
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQ.java
@@ -0,0 +1,142 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt;
+
+import java.util.*;
+
+import org.jivesoftware.smack.packet.*;
+
+/**
+ * Input event IQ. It is used to transfer key and mouse events through XMPP.
+ *
+ * @author Sebastien Vincent
+ */
+public class InputEvtIQ extends IQ {
+
+ /**
+ * The namespace that input event belongs to.
+ */
+ public static final String NAMESPACE =
+ "http://sip-communicator.org/protocol/inputevt";
+
+ /**
+ * The name of the element that contains the input event data.
+ */
+ public static final String ELEMENT_NAME = "inputevt";
+
+ /**
+ * The name of the argument that contains the input action value.
+ */
+ public static final String ACTION_ATTR_NAME = "action";
+
+ /**
+ * Action of this <tt>InputIQ</tt>.
+ */
+ private InputEvtAction action = null;
+
+ /**
+ * List of remote-control elements.
+ */
+ private List<RemoteControlExtension> remoteControls =
+ new ArrayList<RemoteControlExtension>();
+
+ /**
+ * Constructor.
+ */
+ public InputEvtIQ()
+ {
+ }
+
+ /**
+ * Get the XML representation of the IQ.
+ *
+ * @return XML representation of the IQ
+ */
+ @Override
+ public String getChildElementXML()
+ {
+ StringBuilder bldr = new StringBuilder("<" + ELEMENT_NAME);
+
+ bldr.append(" xmlns='" + NAMESPACE + "'");
+
+ bldr.append(" " + ACTION_ATTR_NAME + "='" + getAction() + "'");
+
+ if(remoteControls.size() > 0)
+ {
+ bldr.append(">");
+
+ for(RemoteControlExtension p : remoteControls)
+ bldr.append(p.toXML());
+
+ bldr.append("</" + ELEMENT_NAME + ">");
+ }
+ else
+ {
+ bldr.append("/>");
+ }
+
+ return bldr.toString();
+ }
+
+ /**
+ * Sets the value of this element's <tt>action</tt> attribute. The value of
+ * the 'action' attribute MUST be one of the values enumerated here. If an
+ * entity receives a value not defined here, it MUST ignore the attribute
+ * and MUST return a <tt>bad-request</tt> error to the sender. There is no
+ * default value for the 'action' attribute.
+ *
+ * @param action the value of the <tt>action</tt> attribute.
+ */
+ public void setAction(InputEvtAction action)
+ {
+ this.action = action;
+ }
+
+ /**
+ * Returns the value of this element's <tt>action</tt> attribute. The value
+ * of the 'action' attribute MUST be one of the values enumerated here. If
+ * an entity receives a value not defined here, it MUST ignore the attribute
+ * and MUST return a <tt>bad-request</tt> error to the sender. There is no
+ * default value for the 'action' attribute.
+ *
+ * @return the value of the <tt>action</tt> attribute.
+ */
+ public InputEvtAction getAction()
+ {
+ return action;
+ }
+
+ /**
+ * Add a remote-control extension.
+ *
+ * @param item remote-control extension
+ */
+ public void addRemoteControl(RemoteControlExtension item)
+ {
+ remoteControls.add(item);
+ }
+
+ /**
+ * Remove a remote-control extension.
+ *
+ * @param item remote-control extension
+ */
+ public void removeRemoteControl(RemoteControlExtension item)
+ {
+ remoteControls.remove(item);
+ }
+
+ /**
+ * Get the <tt>RemoteControlExtension</tt> list of this IQ.
+ *
+ * @return list of <tt>RemoteControlExtension</tt>
+ */
+ public List<RemoteControlExtension> getRemoteControls()
+ {
+ return remoteControls;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQProvider.java
new file mode 100644
index 0000000..c2ea6a5
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/InputEvtIQProvider.java
@@ -0,0 +1,85 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt;
+
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smack.provider.*;
+
+import org.xmlpull.v1.*;
+
+/**
+ * An implementation of a InputEvt IQ provider that parses incoming Input IQs.
+ *
+ * @author Sebastien Vincent
+ */
+public class InputEvtIQProvider implements IQProvider
+{
+ /**
+ * Constructs a new InputEvtIQ provider.
+ */
+ public InputEvtIQProvider()
+ {
+/*
+ ProviderManager providerManager = ProviderManager.getInstance();
+
+ providerManager.addExtensionProvider(
+ InputExtensionProvider.ELEMENT_REMOTE_CONTROL,
+ InputExtensionProvider.NAMESPACE,
+ new InputExtensionProvider());
+*/
+ }
+
+ /**
+ * Parse the Input IQ sub-document and returns the corresponding
+ * <tt>InputEvtIQ</tt>.
+ *
+ * @param parser XML parser
+ * @return <tt>InputEvtIQ</tt>
+ * @throws Exception if something goes wrong during parsing
+ */
+ public IQ parseIQ(XmlPullParser parser) throws Exception
+ {
+ InputEvtIQ inputIQ = new InputEvtIQ();
+ boolean done = false;
+ RemoteControlExtensionProvider provider = new RemoteControlExtensionProvider();
+ InputEvtAction action = InputEvtAction.parseString(parser
+ .getAttributeValue("", InputEvtIQ.ACTION_ATTR_NAME));
+
+ inputIQ.setAction(action);
+
+ int eventType;
+ String elementName;
+
+ while (!done)
+ {
+ eventType = parser.next();
+ elementName = parser.getName();
+
+ if (eventType == XmlPullParser.START_TAG)
+ {
+ // <remote-control/>
+ if (elementName.equals(
+ RemoteControlExtensionProvider.ELEMENT_REMOTE_CONTROL))
+ {
+ RemoteControlExtension item =
+ (RemoteControlExtension)provider.parseExtension(parser);
+ inputIQ.addRemoteControl(item);
+ }
+ }
+
+ if (eventType == XmlPullParser.END_TAG)
+ {
+ if (parser.getName().equals(InputEvtIQ.ELEMENT_NAME))
+ {
+ done = true;
+ }
+ }
+ }
+
+ return inputIQ;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtension.java
new file mode 100644
index 0000000..9b9f7c5
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtension.java
@@ -0,0 +1,194 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import org.jivesoftware.smack.packet.*;
+
+/**
+ * This class implements input event extension.
+ *
+ * @author Sebastien Vincent
+ */
+public class RemoteControlExtension
+ implements PacketExtension
+{
+ /**
+ * AWT event that represents our <tt>RemoteControlExtension</tt>.
+ */
+ private final ComponentEvent event;
+
+ /**
+ * Size of the panel that contains video
+ */
+ private final Dimension videoPanelSize;
+
+ /**
+ * Constructor.
+ *
+ */
+ public RemoteControlExtension()
+ {
+ videoPanelSize = null;
+ event = null;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param videoPanelSize size of the panel that contains video
+ */
+ public RemoteControlExtension(Dimension videoPanelSize)
+ {
+ this.videoPanelSize = videoPanelSize;
+ this.event = null;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param event AWT event
+ */
+ public RemoteControlExtension(ComponentEvent event)
+ {
+ this.event = event;
+ this.videoPanelSize = null;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param videoPanelSize size of the panel that contains video
+ * @param event AWT event
+ */
+ public RemoteControlExtension(InputEvent event,
+ Dimension videoPanelSize)
+ {
+ this.videoPanelSize = videoPanelSize;
+ this.event = event;
+ }
+
+ /**
+ * Get <tt>ComponentEvent</tt> that represents our
+ * <tt>InputExtensionItem</tt>.
+ *
+ * @return AWT <tt>ComponentEvent</tt>
+ */
+ public ComponentEvent getEvent()
+ {
+ return event;
+ }
+
+ /**
+ * Get the element name of the <tt>PacketExtension</tt>.
+ *
+ * @return "remote-control"
+ */
+ public String getElementName()
+ {
+ return RemoteControlExtensionProvider.ELEMENT_REMOTE_CONTROL;
+ }
+
+ /**
+ * Returns the XML namespace of the extension sub-packet root element.
+ * The namespace is always "http://sip-communicator.org/protocol/inputevt".
+ *
+ * @return the XML namespace of the packet extension.
+ */
+ public String getNamespace()
+ {
+ return RemoteControlExtensionProvider.NAMESPACE;
+ }
+
+ /**
+ * Get the XML representation.
+ *
+ * @return XML representation of the item
+ */
+ public String toXML()
+ {
+ String ret = null;
+
+ if(event == null)
+ {
+ return null;
+ }
+
+ if(event instanceof MouseEvent)
+ {
+ MouseEvent e = (MouseEvent)event;
+
+ switch(e.getID())
+ {
+ case MouseEvent.MOUSE_DRAGGED:
+ case MouseEvent.MOUSE_MOVED:
+ if(videoPanelSize != null)
+ {
+ Point p = e.getPoint();
+ double x = (p.getX() / videoPanelSize.width);
+ double y = (p.getY() / videoPanelSize.height);
+ ret = RemoteControlExtensionProvider.getMouseMovedXML(x, y);
+ }
+ break;
+ case MouseEvent.MOUSE_WHEEL:
+ MouseWheelEvent ew = (MouseWheelEvent)e;
+ ret = RemoteControlExtensionProvider.getMouseWheelXML(
+ ew.getWheelRotation());
+ break;
+ case MouseEvent.MOUSE_PRESSED:
+ ret = RemoteControlExtensionProvider.getMousePressedXML(
+ e.getModifiers());
+ break;
+ case MouseEvent.MOUSE_RELEASED:
+ ret = RemoteControlExtensionProvider.getMouseReleasedXML(
+ e.getModifiers());
+ break;
+ default:
+ break;
+ }
+ }
+ else if(event instanceof KeyEvent)
+ {
+ KeyEvent e = (KeyEvent)event;
+ int keycode = e.getKeyCode();
+ int key = e.getKeyChar();
+
+ if(key != KeyEvent.CHAR_UNDEFINED)
+ {
+ keycode = e.getKeyChar();
+ }
+ else
+ {
+ keycode = e.getKeyCode();
+ }
+
+ if(keycode == 0)
+ {
+ return null;
+ }
+
+ switch(e.getID())
+ {
+ case KeyEvent.KEY_PRESSED:
+ ret = RemoteControlExtensionProvider.getKeyPressedXML(keycode);
+ break;
+ case KeyEvent.KEY_RELEASED:
+ ret = RemoteControlExtensionProvider.getKeyReleasedXML(keycode);
+ break;
+ case KeyEvent.KEY_TYPED:
+ ret = RemoteControlExtensionProvider.getKeyTypedXML(keycode);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtensionProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtensionProvider.java
new file mode 100644
index 0000000..95394ed
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/inputevt/RemoteControlExtensionProvider.java
@@ -0,0 +1,402 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import org.jivesoftware.smack.packet.*;
+import org.jivesoftware.smack.provider.*;
+import org.xmlpull.v1.*;
+
+/**
+ * This class parses incoming remote-control XML element and extracts
+ * input events such as keyboard and mouse ones.
+ *
+ * @author Sebastien Vincent
+ */
+public class RemoteControlExtensionProvider
+ implements PacketExtensionProvider
+{
+ /**
+ * The name of the remote-info XML element <tt>remote-control</tt>.
+ */
+ public static final String ELEMENT_REMOTE_CONTROL = "remote-control";
+
+ /**
+ * The name of the remote-info XML element <tt>mouse-move</tt>.
+ */
+ public static final String ELEMENT_MOUSE_MOVE = "mouse-move";
+
+ /**
+ * The name of the remote-info XML element <tt>mouse-wheel</tt>.
+ */
+ public static final String ELEMENT_MOUSE_WHEEL = "mouse-wheel";
+
+ /**
+ * The name of the remote-info XML element <tt>mouse-press</tt>.
+ */
+ public static final String ELEMENT_MOUSE_PRESS = "mouse-press";
+
+ /**
+ *The name of the remote-info XML element <tt>mouse-release</tt>.
+ */
+ public static final String ELEMENT_MOUSE_RELEASE = "mouse-release";
+
+ /**
+ * The name of the remote-info XML element <tt>key-press</tt>.
+ */
+ public static final String ELEMENT_KEY_PRESS = "key-press";
+
+ /**
+ * The name of the remote-info XML element <tt>key-release</tt>.
+ */
+ public static final String ELEMENT_KEY_RELEASE = "key-release";
+
+ /**
+ * The name of the remote-info XML element <tt>key-type</tt>.
+ */
+ public static final String ELEMENT_KEY_TYPE = "key-type";
+
+ /**
+ * Namespace of this extension.
+ */
+ public static final String NAMESPACE =
+ "http://sip-communicator.org/protocol/inputevt";
+
+ /**
+ * Component to be used in custom generated <tt>MouseEvent</tt> and
+ * <tt>KeyEvent</tt>.
+ */
+ private static final Component component = new Canvas();
+
+ /**
+ * Constructor.
+ */
+ public RemoteControlExtensionProvider()
+ {
+ }
+
+ /**
+ * Parses the extension and returns a <tt>PacketExtension</tt>.
+ *
+ * @param parser XML parser
+ * @return a <tt>PacketExtension</tt> that represents a remote-control
+ * element.
+ * @throws Exception if an error occurs during XML parsing
+ */
+ public PacketExtension parseExtension(XmlPullParser parser)
+ throws Exception
+ {
+ RemoteControlExtension result = null;
+ boolean done = false;
+
+ while (!done)
+ {
+ try
+ {
+ int eventType = parser.next();
+
+ if (eventType == XmlPullParser.START_TAG)
+ {
+ if(parser.getName().equals(ELEMENT_MOUSE_MOVE))
+ {
+ String attr = parser.getAttributeValue("", "x");
+ String attr2 = parser.getAttributeValue("", "y");
+ if(attr != null && attr2 != null)
+ {
+ int x = (int)(Double.
+ parseDouble(attr) * 1000);
+ int y = (int)(Double.
+ parseDouble(attr2) * 1000);
+
+ MouseEvent me = new MouseEvent(component,
+ MouseEvent.MOUSE_MOVED,
+ System.currentTimeMillis(),
+ 0, x, y, 0, false, 0);
+
+ result = new RemoteControlExtension(me);
+ continue;
+ }
+ }
+
+ if(parser.getName().equals(ELEMENT_MOUSE_WHEEL))
+ {
+ String attr = parser.getAttributeValue("", "notch");
+ if(attr != null)
+ {
+ MouseWheelEvent me = new MouseWheelEvent(
+ component, MouseEvent.MOUSE_WHEEL,
+ System.currentTimeMillis(),
+ 0, 0, 0, 0, false, 0, 0,
+ Integer.parseInt(attr));
+
+
+ result = new RemoteControlExtension(me);
+ continue;
+ }
+ }
+
+ if(parser.getName().equals(ELEMENT_MOUSE_PRESS))
+ {
+ String attr = parser.getAttributeValue("", "btns");
+ if(attr != null)
+ {
+ MouseEvent me = new MouseEvent(component,
+ MouseEvent.MOUSE_PRESSED,
+ System.currentTimeMillis(),
+ Integer.parseInt(attr),
+ 0, 0, 0, false, 0);
+
+ result = new RemoteControlExtension(me);
+ continue;
+ }
+ }
+
+ if(parser.getName().equals(ELEMENT_MOUSE_RELEASE))
+ {
+ String attr = parser.getAttributeValue("", "btns");
+ if(attr != null)
+ {
+ MouseEvent me = new MouseEvent(component,
+ MouseEvent.MOUSE_RELEASED,
+ System.currentTimeMillis(),
+ Integer.parseInt(attr),
+ 0, 0, 0, false, 0);
+
+ result = new RemoteControlExtension(me);
+ continue;
+ }
+ }
+
+ if(parser.getName().equals(ELEMENT_KEY_PRESS))
+ {
+ String attr = parser.getAttributeValue("", "keycode");
+ if(attr != null)
+ {
+ KeyEvent ke = new KeyEvent(component,
+ KeyEvent.KEY_PRESSED,
+ System.currentTimeMillis(),
+ 0,
+ Integer.parseInt(attr),
+ (char)0);
+
+ result = new RemoteControlExtension(ke);
+ continue;
+ }
+ }
+
+ if(parser.getName().equals(ELEMENT_KEY_RELEASE))
+ {
+ String attr = parser.getAttributeValue("", "keycode");
+ if(attr != null)
+ {
+ KeyEvent ke = new KeyEvent(component,
+ KeyEvent.KEY_RELEASED,
+ System.currentTimeMillis(),
+ 0,
+ Integer.parseInt(attr),
+ (char)0);
+
+ result = new RemoteControlExtension(ke);
+ continue;
+ }
+ }
+
+ if(parser.getName().equals(ELEMENT_KEY_TYPE))
+ {
+ String attr = parser.getAttributeValue("", "keychar");
+ if(attr != null)
+ {
+ KeyEvent ke = new KeyEvent(component,
+ KeyEvent.KEY_TYPED,
+ System.currentTimeMillis(),
+ 0,
+ 0,
+ (char)Integer.parseInt(attr));
+
+ result = new RemoteControlExtension(ke);
+ continue;
+ }
+ }
+ }
+ else if (eventType == XmlPullParser.END_TAG)
+ {
+ if (parser.getName().equals(
+ RemoteControlExtensionProvider.ELEMENT_REMOTE_CONTROL))
+ {
+ done = true;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ if(result == null)
+ {
+ /* we are not allowed to return null otherwise the parser goes
+ * crazy
+ */
+ result = new RemoteControlExtension(new ComponentEvent(component, 0));
+ }
+
+ return result;
+ }
+
+ /**
+ * Appends a specific array of <tt>String</tt>s to a specific
+ * <tt>StringBuffer</tt>.
+ *
+ * @param stringBuffer the <tt>StringBuffer</tt> to append the specified
+ * <tt>strings</tt> to
+ * @param strings the <tt>String</tt> values to be appended to the specified
+ * <tt>stringBuffer</tt>
+ */
+ private static void append(StringBuffer stringBuffer, String... strings)
+ {
+ for (String str : strings)
+ stringBuffer.append(str);
+ }
+
+ /**
+ * Build a key-press remote-control XML element.
+ *
+ * @param keycode keyboard's code
+ * @return raw XML bytes
+ */
+ public static String getKeyPressedXML(int keycode)
+ {
+ StringBuffer xml = new StringBuffer();
+
+ append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE
+ + "\">");
+ // <key-press>
+ append(xml, "<", RemoteControlExtensionProvider.ELEMENT_KEY_PRESS);
+ append(xml, " keycode=\"", Integer.toString(keycode), "\"/>");
+ append(xml, "</" + ELEMENT_REMOTE_CONTROL + ">");
+ return xml.toString();
+ }
+
+ /**
+ * Build a key-release remote-control XML element.
+ *
+ * @param keycode keyboard's code
+ * @return raw XML bytes
+ */
+ public static String getKeyReleasedXML(int keycode)
+ {
+ StringBuffer xml = new StringBuffer();
+
+ append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE
+ + "\">");
+ // <key-release>
+ append(xml, "<", RemoteControlExtensionProvider.ELEMENT_KEY_RELEASE);
+ append(xml, " keycode=\"", Integer.toString(keycode), "\"/>");
+ append(xml, "</" + ELEMENT_REMOTE_CONTROL + ">");
+ return xml.toString();
+ }
+
+ /**
+ * Build a key-typed remote-control XML element.
+ *
+ * @param keycode keyboard's code
+ * @return raw XML bytes
+ */
+ public static String getKeyTypedXML(int keycode)
+ {
+ StringBuffer xml = new StringBuffer();
+
+ append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE
+ + "\">");
+ // <key-typed>
+ append(xml, "<", RemoteControlExtensionProvider.ELEMENT_KEY_TYPE);
+ append(xml, " keychar=\"", Integer.toString(keycode), "\"/>");
+ append(xml, "</" + ELEMENT_REMOTE_CONTROL + ">");
+ return xml.toString();
+ }
+
+ /**
+ * Build a mouse-press remote-control XML element.
+ *
+ * @param btns button mask
+ * @return raw XML bytes
+ */
+ public static String getMousePressedXML(int btns)
+ {
+ StringBuffer xml = new StringBuffer();
+
+ append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE
+ + "\">");
+ // <mouse-press>
+ append(xml, "<", RemoteControlExtensionProvider.ELEMENT_MOUSE_PRESS);
+ append(xml, " btns=\"", Integer.toString(btns), "\"/>");
+ append(xml, "</" + ELEMENT_REMOTE_CONTROL + ">");
+ return xml.toString();
+ }
+
+ /**
+ * Build a remote-info mouse-release remote-control XML element.
+ *
+ * @param btns button mask
+ * @return raw XML bytes
+ */
+ public static String getMouseReleasedXML(int btns)
+ {
+ StringBuffer xml = new StringBuffer();
+
+ append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE
+ + "\">");
+ // <mouse-release>
+ append(xml, "<", RemoteControlExtensionProvider.ELEMENT_MOUSE_RELEASE);
+ append(xml, " btns=\"", Integer.toString(btns), "\"/>");
+ append(xml, "</" + ELEMENT_REMOTE_CONTROL + ">");
+ return xml.toString();
+ }
+
+ /**
+ * Build a remote-info mouse-move remote-control XML element.
+ *
+ * @param x x position of the mouse
+ * @param y y position of the mouse
+ * @return raw XML bytes
+ */
+ public static String getMouseMovedXML(double x, double y)
+ {
+ StringBuffer xml = new StringBuffer();
+
+ append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE
+ + "\">");
+ // <mouse-press>
+ append(xml, "<", RemoteControlExtensionProvider.ELEMENT_MOUSE_MOVE);
+ append(xml, " x=\"", Double.toString(x), "\" y=\"", Double.toString(y),
+ "\"/>");
+ append(xml, "</" + ELEMENT_REMOTE_CONTROL + ">");
+ return xml.toString();
+ }
+
+ /**
+ * Build a remote-info mouse-wheel remote-control XML element.
+ *
+ * @param notch wheel notch
+ * @return raw XML bytes
+ */
+ public static String getMouseWheelXML(int notch)
+ {
+ StringBuffer xml = new StringBuffer();
+
+ append(xml, "<" + ELEMENT_REMOTE_CONTROL + " xmlns=\"" + NAMESPACE
+ + "\">");
+ // <mouse-wheel>
+ append(xml, "<", RemoteControlExtensionProvider.ELEMENT_MOUSE_WHEEL);
+ append(xml, " notch=\"", Integer.toString(notch), "\"/>");
+ append(xml, "</" + ELEMENT_REMOTE_CONTROL + ">");
+ return xml.toString();
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/InputEvtPacketExtension.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/InputEvtPacketExtension.java
new file mode 100644
index 0000000..7abced8
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/InputEvtPacketExtension.java
@@ -0,0 +1,37 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle;
+
+import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
+
+/**
+ * Represents the content <tt>inputevt</tt> element that may be find in
+ * <tt>content</tt> part of a Jingle media negociation.
+ *
+ * @author Sebastien Vincent
+ */
+public class InputEvtPacketExtension extends AbstractPacketExtension
+{
+ /**
+ * Name of the XML element representing the extension.
+ */
+ public final static String ELEMENT_NAME = "inputevt";
+
+ /**
+ * Namespace..
+ */
+ public final static String NAMESPACE =
+ "http://sip-communicator.org/protocol/inputevt";
+
+ /**
+ * Constructs a new <tt>inputevt</tt> extension.
+ */
+ public InputEvtPacketExtension()
+ {
+ super(NAMESPACE, ELEMENT_NAME);
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java
index 921b940..910f4e8 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/extensions/jingle/JingleIQProvider.java
@@ -5,6 +5,7 @@
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.protocol.jabber.extensions.jingle;
+
import java.util.logging.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
@@ -12,7 +13,6 @@ import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
import org.jivesoftware.smack.provider.*;
import org.xmlpull.v1.XmlPullParser;
-
/**
* An implementation of a Jingle IQ provider that parses incoming Jingle IQs.
*
@@ -107,6 +107,13 @@ public class JingleIQProvider implements IQProvider
IceUdpTransportPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider<RemoteCandidatePacketExtension>(
RemoteCandidatePacketExtension.class));
+
+ //inputevt <inputevt/> provider
+ providerManager.addExtensionProvider(
+ InputEvtPacketExtension.ELEMENT_NAME,
+ InputEvtPacketExtension.NAMESPACE,
+ new DefaultPacketExtensionProvider<InputEvtPacketExtension>(
+ InputEvtPacketExtension.class));
}
/**
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf
index 5ff04de..2037109 100755
--- a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf
@@ -41,6 +41,7 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.neomedia.device,
net.java.sip.communicator.service.neomedia.event,
net.java.sip.communicator.service.neomedia.format,
+ net.java.sip.communicator.service.hid,
net.java.sip.communicator.service.netaddr,
net.java.sip.communicator.service.argdelegation,
net.java.sip.communicator.service.gui,