aboutsummaryrefslogtreecommitdiff
path: root/tools/EventClients/lib
diff options
context:
space:
mode:
authorAlTheKiller <AlTheKiller@svn>2009-09-23 01:49:50 +0000
committerAlTheKiller <AlTheKiller@svn>2009-09-23 01:49:50 +0000
commit45285e8a9300cd754a760560640b75b09f98035e (patch)
treead9f093885ad5c98e9dd4156674e7691c22ed0a2 /tools/EventClients/lib
step 3/4: Move linuxport to trunk. How'd I get roped into this?
git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/trunk@23097 568bbfeb-2a22-0410-94d2-cc84cf5bfa90
Diffstat (limited to 'tools/EventClients/lib')
-rw-r--r--tools/EventClients/lib/c#/EventClient.cs529
-rw-r--r--tools/EventClients/lib/c++/xbmcclient.h816
-rwxr-xr-xtools/EventClients/lib/java/build.xml72
-rwxr-xr-xtools/EventClients/lib/java/src/org/xbmc/eventclient/Packet.java271
-rwxr-xr-xtools/EventClients/lib/java/src/org/xbmc/eventclient/PacketACTION.java43
-rwxr-xr-xtools/EventClients/lib/java/src/org/xbmc/eventclient/PacketBUTTON.java139
-rwxr-xr-xtools/EventClients/lib/java/src/org/xbmc/eventclient/PacketBYE.java19
-rwxr-xr-xtools/EventClients/lib/java/src/org/xbmc/eventclient/PacketHELO.java46
-rwxr-xr-xtools/EventClients/lib/java/src/org/xbmc/eventclient/PacketLOG.java30
-rwxr-xr-xtools/EventClients/lib/java/src/org/xbmc/eventclient/PacketMOUSE.java27
-rwxr-xr-xtools/EventClients/lib/java/src/org/xbmc/eventclient/PacketNOTIFICATION.java53
-rwxr-xr-xtools/EventClients/lib/java/src/org/xbmc/eventclient/PacketPING.java19
-rwxr-xr-xtools/EventClients/lib/java/src/org/xbmc/eventclient/XBMCClient.java300
-rw-r--r--tools/EventClients/lib/python/__init__.py0
-rw-r--r--tools/EventClients/lib/python/bt/__init__.py0
-rw-r--r--tools/EventClients/lib/python/bt/bt.py65
-rw-r--r--tools/EventClients/lib/python/bt/hid.py54
-rw-r--r--tools/EventClients/lib/python/ps3/__init__.py0
-rw-r--r--tools/EventClients/lib/python/ps3/keymaps.py118
-rw-r--r--tools/EventClients/lib/python/ps3/sixaxis.py206
-rw-r--r--tools/EventClients/lib/python/xbmcclient.py621
-rw-r--r--tools/EventClients/lib/python/zeroconf.py141
22 files changed, 3569 insertions, 0 deletions
diff --git a/tools/EventClients/lib/c#/EventClient.cs b/tools/EventClients/lib/c#/EventClient.cs
new file mode 100644
index 0000000000..f904dcaa93
--- /dev/null
+++ b/tools/EventClients/lib/c#/EventClient.cs
@@ -0,0 +1,529 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+
+namespace XBMC
+{
+
+ public enum IconType
+ {
+ ICON_NONE = 0x00,
+ ICON_JPEG = 0x01,
+ ICON_PNG = 0x02,
+ ICON_GIF = 0x03
+ }
+
+ public enum ButtonFlagsType
+ {
+ BTN_USE_NAME = 0x01,
+ BTN_DOWN = 0x02,
+ BTN_UP = 0x04,
+ BTN_USE_AMOUNT = 0x08,
+ BTN_QUEUE = 0x10,
+ BTN_NO_REPEAT = 0x20,
+ BTN_VKEY = 0x40,
+ BTN_AXIS = 0x80
+ }
+
+ public enum MouseFlagsType
+ {
+ MS_ABSOLUTE = 0x01
+ }
+
+ public enum LogTypeEnum
+ {
+ LOGDEBUG = 0,
+ LOGINFO = 1,
+ LOGNOTICE = 2,
+ LOGWARNING = 3,
+ LOGERROR = 4,
+ LOGSEVERE = 5,
+ LOGFATAL = 6,
+ LOGNONE = 7
+ }
+
+ public enum ActionType
+ {
+ ACTION_EXECBUILTIN = 0x01,
+ ACTION_BUTTON = 0x02
+ }
+
+ public class EventClient
+ {
+
+ /************************************************************************/
+ /* Written by Peter Tribe aka EqUiNox (TeamBlackbolt) */
+ /* Based upon XBMC's xbmcclient.cpp class */
+ /************************************************************************/
+
+ private enum PacketType
+ {
+ PT_HELO = 0x01,
+ PT_BYE = 0x02,
+ PT_BUTTON = 0x03,
+ PT_MOUSE = 0x04,
+ PT_PING = 0x05,
+ PT_BROADCAST = 0x06, //Currently not implemented
+ PT_NOTIFICATION = 0x07,
+ PT_BLOB = 0x08,
+ PT_LOG = 0x09,
+ PT_ACTION = 0x0A,
+ PT_DEBUG = 0xFF //Currently not implemented
+ }
+
+ private const int STD_PORT = 9777;
+ private const int MAX_PACKET_SIZE = 1024;
+ private const int HEADER_SIZE = 32;
+ private const int MAX_PAYLOAD_SIZE = MAX_PACKET_SIZE - HEADER_SIZE;
+ private const byte MAJOR_VERSION = 2;
+ private const byte MINOR_VERSION = 0;
+
+ private uint uniqueToken;
+ private Socket socket;
+
+ public bool Connect(string Address)
+ {
+ return Connect(Address, STD_PORT, (uint)System.DateTime.Now.TimeOfDay.Milliseconds);
+ }
+
+ public bool Connect(string Address, int Port)
+ {
+ return Connect(Address, Port, (uint)System.DateTime.Now.TimeOfDay.Milliseconds);
+ }
+
+
+ public bool Connect(string Address, int Port, uint UID)
+ {
+ try
+ {
+ if (socket != null) Disconnect();
+ uniqueToken = UID;
+ socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+ socket.Connect(Dns.GetHostByName(Address).AddressList[0].ToString(), Port);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public bool Connected
+ {
+ get
+ {
+ if (socket == null) return false;
+ return socket.Connected;
+ }
+ }
+
+ public void Disconnect()
+ {
+ try
+ {
+ if (socket != null)
+ {
+ socket.Shutdown(SocketShutdown.Both);
+ socket.Close();
+ socket = null;
+ }
+ }
+ catch
+ {
+ }
+ }
+
+ private byte[] Header(PacketType PacketType, int NumberOfPackets, int CurrentPacket, int PayloadSize)
+ {
+
+ byte[] header = new byte[HEADER_SIZE];
+
+ header[0] = (byte)'X';
+ header[1] = (byte)'B';
+ header[2] = (byte)'M';
+ header[3] = (byte)'C';
+
+ header[4] = MAJOR_VERSION;
+ header[5] = MINOR_VERSION;
+
+ if (CurrentPacket == 1)
+ {
+ header[6] = (byte)(((ushort)PacketType & 0xff00) >> 8);
+ header[7] = (byte)((ushort)PacketType & 0x00ff);
+ }
+ else
+ {
+ header[6] = (byte)(((ushort)PacketType.PT_BLOB & 0xff00) >> 8);
+ header[7] = (byte)((ushort)PacketType.PT_BLOB & 0x00ff);
+ }
+
+ header[8] = (byte)((CurrentPacket & 0xff000000) >> 24);
+ header[9] = (byte)((CurrentPacket & 0x00ff0000) >> 16);
+ header[10] = (byte)((CurrentPacket & 0x0000ff00) >> 8);
+ header[11] = (byte)(CurrentPacket & 0x000000ff);
+
+ header[12] = (byte)((NumberOfPackets & 0xff000000) >> 24);
+ header[13] = (byte)((NumberOfPackets & 0x00ff0000) >> 16);
+ header[14] = (byte)((NumberOfPackets & 0x0000ff00) >> 8);
+ header[15] = (byte)(NumberOfPackets & 0x000000ff);
+
+ header[16] = (byte)((PayloadSize & 0xff00) >> 8);
+ header[17] = (byte)(PayloadSize & 0x00ff);
+
+ header[18] = (byte)((uniqueToken & 0xff000000) >> 24);
+ header[19] = (byte)((uniqueToken & 0x00ff0000) >> 16);
+ header[20] = (byte)((uniqueToken & 0x0000ff00) >> 8);
+ header[21] = (byte)(uniqueToken & 0x000000ff);
+
+ return header;
+
+ }
+
+ private bool Send(PacketType PacketType, byte[] Payload)
+ {
+ try
+ {
+
+ bool successfull = true;
+ int packetCount = (Payload.Length / MAX_PAYLOAD_SIZE) + 1;
+ int bytesToSend = 0;
+ int bytesSent = 0;
+ int bytesLeft = Payload.Length;
+
+ for (int Package = 1; Package <= packetCount; Package++)
+ {
+
+ if (bytesLeft > MAX_PAYLOAD_SIZE)
+ {
+ bytesToSend = MAX_PAYLOAD_SIZE;
+ bytesLeft -= bytesToSend;
+ }
+ else
+ {
+ bytesToSend = bytesLeft;
+ bytesLeft = 0;
+ }
+
+ byte[] header = Header(PacketType, packetCount, Package, bytesToSend);
+ byte[] packet = new byte[MAX_PACKET_SIZE];
+
+ Array.Copy(header, 0, packet, 0, header.Length);
+ Array.Copy(Payload, bytesSent, packet, header.Length, bytesToSend);
+
+ int sendSize = socket.Send(packet, header.Length + bytesToSend, SocketFlags.None);
+
+ if (sendSize != (header.Length + bytesToSend))
+ {
+ successfull = false;
+ break;
+ }
+
+ bytesSent += bytesToSend;
+
+ }
+
+ return successfull;
+
+ }
+ catch
+ {
+
+ return false;
+
+ }
+
+ }
+
+ /************************************************************************/
+ /* SendHelo - Payload format */
+ /* %s - device name (max 128 chars) */
+ /* %c - icontype ( 0=>NOICON, 1=>JPEG , 2=>PNG , 3=>GIF ) */
+ /* %s - my port ( 0=>not listening ) */
+ /* %d - reserved1 ( 0 ) */
+ /* %d - reserved2 ( 0 ) */
+ /* XX - imagedata ( can span multiple packets ) */
+ /************************************************************************/
+ public bool SendHelo(string DevName, IconType IconType, string IconFile)
+ {
+
+ byte[] icon = new byte[0];
+ if (IconType != IconType.ICON_NONE)
+ icon = File.ReadAllBytes(IconFile);
+
+ byte[] payload = new byte[DevName.Length + 12 + icon.Length];
+
+ int offset = 0;
+
+ for (int i = 0; i < DevName.Length; i++)
+ payload[offset++] = (byte)DevName[i];
+ payload[offset++] = (byte)'\0';
+
+ payload[offset++] = (byte)IconType;
+
+ payload[offset++] = (byte)0;
+ payload[offset++] = (byte)'\0';
+
+ for (int i = 0; i < 8; i++)
+ payload[offset++] = (byte)0;
+
+ Array.Copy(icon, 0, payload, DevName.Length + 12, icon.Length);
+
+ return Send(PacketType.PT_HELO, payload);
+
+ }
+
+ public bool SendHelo(string DevName)
+ {
+ return SendHelo(DevName, IconType.ICON_NONE, "");
+ }
+
+ /************************************************************************/
+ /* SendNotification - Payload format */
+ /* %s - caption */
+ /* %s - message */
+ /* %c - icontype ( 0=>NOICON, 1=>JPEG , 2=>PNG , 3=>GIF ) */
+ /* %d - reserved ( 0 ) */
+ /* XX - imagedata ( can span multiple packets ) */
+ /************************************************************************/
+ public bool SendNotification(string Caption, string Message, IconType IconType, string IconFile)
+ {
+
+ byte[] icon = new byte[0];
+ if (IconType != IconType.ICON_NONE)
+ icon = File.ReadAllBytes(IconFile);
+
+ byte[] payload = new byte[Caption.Length + Message.Length + 7 + icon.Length];
+
+ int offset = 0;
+
+ for (int i = 0; i < Caption.Length; i++)
+ payload[offset++] = (byte)Caption[i];
+ payload[offset++] = (byte)'\0';
+
+ for (int i = 0; i < Message.Length; i++)
+ payload[offset++] = (byte)Message[i];
+ payload[offset++] = (byte)'\0';
+
+ payload[offset++] = (byte)IconType;
+
+ for (int i = 0; i < 4; i++)
+ payload[offset++] = (byte)0;
+
+ Array.Copy(icon, 0, payload, Caption.Length + Message.Length + 7, icon.Length);
+
+ return Send(PacketType.PT_NOTIFICATION, payload);
+
+ }
+
+ public bool SendNotification(string Caption, string Message)
+ {
+ return SendNotification(Caption, Message, IconType.ICON_NONE, "");
+ }
+
+ /************************************************************************/
+ /* SendButton - Payload format */
+ /* %i - button code */
+ /* %i - flags 0x01 => use button map/name instead of code */
+ /* 0x02 => btn down */
+ /* 0x04 => btn up */
+ /* 0x08 => use amount */
+ /* 0x10 => queue event */
+ /* 0x20 => do not repeat */
+ /* 0x40 => virtual key */
+ /* 0x80 => axis key */
+ /* %i - amount ( 0 => 65k maps to -1 => 1 ) */
+ /* %s - device map (case sensitive and required if flags & 0x01) */
+ /* "KB" - Standard keyboard map */
+ /* "XG" - Xbox Gamepad */
+ /* "R1" - Xbox Remote */
+ /* "R2" - Xbox Universal Remote */
+ /* "LI:devicename" - valid LIRC device map where 'devicename' */
+ /* is the actual name of the LIRC device */
+ /* "JS<num>:joyname" - valid Joystick device map where */
+ /* 'joyname' is the name specified in */
+ /* the keymap. JS only supports button code */
+ /* and not button name currently (!0x01). */
+ /* %s - button name (required if flags & 0x01) */
+ /************************************************************************/
+ private bool SendButton(string Button, ushort ButtonCode, string DeviceMap, ButtonFlagsType Flags, short Amount)
+ {
+
+ if (Button.Length != 0)
+ {
+ if ((Flags & ButtonFlagsType.BTN_USE_NAME) == 0)
+ Flags |= ButtonFlagsType.BTN_USE_NAME;
+ ButtonCode = 0;
+ }
+ else
+ Button = "";
+
+ if (Amount > 0)
+ {
+ if ((Flags & ButtonFlagsType.BTN_USE_AMOUNT) == 0)
+ Flags |= ButtonFlagsType.BTN_USE_AMOUNT;
+ }
+
+ if ((Flags & ButtonFlagsType.BTN_DOWN) == 0 || (Flags & ButtonFlagsType.BTN_UP) == 0)
+ Flags |= ButtonFlagsType.BTN_DOWN;
+
+ byte[] payload = new byte[Button.Length + DeviceMap.Length + 8];
+
+ int offset = 0;
+
+ payload[offset++] = (byte)((ButtonCode & 0xff00) >> 8);
+ payload[offset++] = (byte)(ButtonCode & 0x00ff);
+
+ payload[offset++] = (byte)(((ushort)Flags & 0xff00) >> 8);
+ payload[offset++] = (byte)((ushort)Flags & 0x00ff);
+
+ payload[offset++] = (byte)((Amount & 0xff00) >> 8);
+ payload[offset++] = (byte)(Amount & 0x00ff);
+
+ for (int i = 0; i < DeviceMap.Length; i++)
+ payload[offset++] = (byte)DeviceMap[i];
+ payload[offset++] = (byte)'\0';
+
+ for (int i = 0; i < Button.Length; i++)
+ payload[offset++] = (byte)Button[i];
+ payload[offset++] = (byte)'\0';
+
+ return Send(PacketType.PT_BUTTON, payload);
+
+ }
+
+ public bool SendButton(string Button, string DeviceMap, ButtonFlagsType Flags, short Amount)
+ {
+ return SendButton(Button, 0, DeviceMap, Flags, Amount);
+ }
+
+ public bool SendButton(string Button, string DeviceMap, ButtonFlagsType Flags)
+ {
+ return SendButton(Button, 0, DeviceMap, Flags, 0);
+ }
+
+ public bool SendButton(ushort ButtonCode, string DeviceMap, ButtonFlagsType Flags, short Amount)
+ {
+ return SendButton("", ButtonCode, DeviceMap, Flags, Amount);
+ }
+
+ public bool SendButton(ushort ButtonCode, string DeviceMap, ButtonFlagsType Flags)
+ {
+ return SendButton("", ButtonCode, DeviceMap, Flags, 0);
+ }
+
+ public bool SendButton(ushort ButtonCode, ButtonFlagsType Flags, short Amount)
+ {
+ return SendButton("", ButtonCode, "", Flags, Amount);
+ }
+
+ public bool SendButton(ushort ButtonCode, ButtonFlagsType Flags)
+ {
+ return SendButton("", ButtonCode, "", Flags, 0);
+ }
+
+ public bool SendButton()
+ {
+ return SendButton("", 0, "", ButtonFlagsType.BTN_UP, 0);
+ }
+
+ /************************************************************************/
+ /* SendPing - No payload */
+ /************************************************************************/
+ public bool SendPing()
+ {
+ byte[] payload = new byte[0];
+ return Send(PacketType.PT_PING, payload);
+ }
+
+ /************************************************************************/
+ /* SendBye - No payload */
+ /************************************************************************/
+ public bool SendBye()
+ {
+ byte[] payload = new byte[0];
+ return Send(PacketType.PT_BYE, payload);
+ }
+
+ /************************************************************************/
+ /* SendMouse - Payload format */
+ /* %c - flags */
+ /* - 0x01 absolute position */
+ /* %i - mousex (0-65535 => maps to screen width) */
+ /* %i - mousey (0-65535 => maps to screen height) */
+ /************************************************************************/
+ public bool SendMouse(ushort X, ushort Y, MouseFlagsType Flags)
+ {
+
+ byte[] payload = new byte[9];
+
+ int offset = 0;
+
+ payload[offset++] = (byte)Flags;
+
+ payload[offset++] = (byte)((X & 0xff00) >> 8);
+ payload[offset++] = (byte)(X & 0x00ff);
+
+ payload[offset++] = (byte)((Y & 0xff00) >> 8);
+ payload[offset++] = (byte)(Y & 0x00ff);
+
+ return Send(PacketType.PT_MOUSE, payload);
+
+ }
+
+ public bool SendMouse(ushort X, ushort Y)
+ {
+ return SendMouse(X, Y, MouseFlagsType.MS_ABSOLUTE);
+ }
+
+ /************************************************************************/
+ /* SendLog - Payload format */
+ /* %c - log type */
+ /* %s - message */
+ /************************************************************************/
+ public bool SendLog(LogTypeEnum LogLevel, string Message)
+ {
+
+ byte[] payload = new byte[Message.Length + 2];
+
+ int offset = 0;
+
+ payload[offset++] = (byte)LogLevel;
+
+ for (int i = 0; i < Message.Length; i++)
+ payload[offset++] = (byte)Message[i];
+ payload[offset++] = (byte)'\0';
+
+ return Send(PacketType.PT_LOG, payload);
+
+ }
+
+ /************************************************************************/
+ /* SendAction - Payload format */
+ /* %c - action type */
+ /* %s - action message */
+ /************************************************************************/
+ public bool SendAction(ActionType Action, string Message)
+ {
+
+ byte[] payload = new byte[Message.Length + 2];
+
+ int offset = 0;
+
+ payload[offset++] = (byte)Action;
+
+ for (int i = 0; i < Message.Length; i++)
+ payload[offset++] = (byte)Message[i];
+ payload[offset++] = (byte)'\0';
+
+ return Send(PacketType.PT_ACTION, payload);
+
+ }
+
+ public bool SendAction(string Message)
+ {
+ return SendAction(ActionType.ACTION_EXECBUILTIN, Message);
+ }
+
+ }
+}
diff --git a/tools/EventClients/lib/c++/xbmcclient.h b/tools/EventClients/lib/c++/xbmcclient.h
new file mode 100644
index 0000000000..f7456baef8
--- /dev/null
+++ b/tools/EventClients/lib/c++/xbmcclient.h
@@ -0,0 +1,816 @@
+#ifndef __XBMC_CLIENT_H__
+#define __XBMC_CLIENT_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifdef _WIN32
+#include <winsock.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#endif
+#include <vector>
+#include <iostream>
+#include <fstream>
+#include <time.h>
+
+#define STD_PORT 9777
+
+#define MS_ABSOLUTE 0x01
+//#define MS_RELATIVE 0x02
+
+#define BTN_USE_NAME 0x01
+#define BTN_DOWN 0x02
+#define BTN_UP 0x04
+#define BTN_USE_AMOUNT 0x08
+#define BTN_QUEUE 0x10
+#define BTN_NO_REPEAT 0x20
+#define BTN_VKEY 0x40
+#define BTN_AXIS 0x80
+
+#define PT_HELO 0x01
+#define PT_BYE 0x02
+#define PT_BUTTON 0x03
+#define PT_MOUSE 0x04
+#define PT_PING 0x05
+#define PT_BROADCAST 0x06
+#define PT_NOTIFICATION 0x07
+#define PT_BLOB 0x08
+#define PT_LOG 0x09
+#define PT_ACTION 0x0A
+#define PT_DEBUG 0xFF
+
+#define ICON_NONE 0x00
+#define ICON_JPEG 0x01
+#define ICON_PNG 0x02
+#define ICON_GIF 0x03
+
+#define MAX_PACKET_SIZE 1024
+#define HEADER_SIZE 32
+#define MAX_PAYLOAD_SIZE (MAX_PACKET_SIZE - HEADER_SIZE)
+
+#define MAJOR_VERSION 2
+#define MINOR_VERSION 0
+
+#define LOGDEBUG 0
+#define LOGINFO 1
+#define LOGNOTICE 2
+#define LOGWARNING 3
+#define LOGERROR 4
+#define LOGSEVERE 5
+#define LOGFATAL 6
+#define LOGNONE 7
+
+#define ACTION_EXECBUILTIN 0x01
+#define ACTION_BUTTON 0x02
+
+class CAddress
+{
+private:
+ struct sockaddr_in m_Addr;
+public:
+ CAddress(int Port = STD_PORT)
+ {
+ m_Addr.sin_family = AF_INET;
+ m_Addr.sin_port = htons(Port);
+ m_Addr.sin_addr.s_addr = INADDR_ANY;
+ memset(m_Addr.sin_zero, '\0', sizeof m_Addr.sin_zero);
+ }
+
+ CAddress(const char *Address, int Port = STD_PORT)
+ {
+ m_Addr.sin_port = htons(Port);
+
+ struct hostent *h;
+ if (Address == NULL || (h=gethostbyname(Address)) == NULL)
+ {
+ if (Address != NULL)
+ printf("Error: Get host by name\n");
+
+ m_Addr.sin_addr.s_addr = INADDR_ANY;
+ m_Addr.sin_family = AF_INET;
+ }
+ else
+ {
+ m_Addr.sin_family = h->h_addrtype;
+ m_Addr.sin_addr = *((struct in_addr *)h->h_addr);
+ }
+ memset(m_Addr.sin_zero, '\0', sizeof m_Addr.sin_zero);
+ }
+
+ void SetPort(int port)
+ {
+ m_Addr.sin_port = htons(port);
+ }
+
+ const sockaddr *GetAddress()
+ {
+ return ((struct sockaddr *)&m_Addr);
+ }
+
+ bool Bind(int Sockfd)
+ {
+ return (bind(Sockfd, (struct sockaddr *)&m_Addr, sizeof m_Addr) == 0);
+ }
+};
+
+class XBMCClientUtils
+{
+public:
+ XBMCClientUtils() {}
+ ~XBMCClientUtils() {}
+ static unsigned int GetUniqueIdentifier()
+ {
+ static time_t id = time(NULL);
+ return id;
+ }
+
+ static void Clean()
+ {
+ #ifdef _WIN32
+ WSACleanup();
+ #endif
+ }
+
+ static bool Initialize()
+ {
+ #ifdef _WIN32
+ WSADATA wsaData;
+ if (WSAStartup(MAKEWORD(1, 1), &wsaData))
+ return false;
+ #endif
+ return true;
+ }
+};
+
+class CPacket
+{
+/* Base class that implements a single event packet.
+
+ - Generic packet structure (maximum 1024 bytes per packet)
+ - Header is 32 bytes long, so 992 bytes available for payload
+ - large payloads can be split into multiple packets using H4 and H5
+ H5 should contain total no. of packets in such a case
+ - H6 contains length of P1, which is limited to 992 bytes
+ - if H5 is 0 or 1, then H4 will be ignored (single packet msg)
+ - H7 must be set to zeros for now
+
+ -----------------------------
+ | -H1 Signature ("XBMC") | - 4 x CHAR 4B
+ | -H2 Version (eg. 2.0) | - 2 x UNSIGNED CHAR 2B
+ | -H3 PacketType | - 1 x UNSIGNED SHORT 2B
+ | -H4 Sequence number | - 1 x UNSIGNED LONG 4B
+ | -H5 No. of packets in msg | - 1 x UNSIGNED LONG 4B
+ | -H6 Payload size | - 1 x UNSIGNED SHORT 2B
+ | -H7 Client's unique token | - 1 x UNSIGNED LONG 4B
+ | -H8 Reserved | - 10 x UNSIGNED CHAR 10B
+ |---------------------------|
+ | -P1 payload | -
+ -----------------------------
+*/
+public:
+ CPacket()
+ {
+ m_PacketType = 0;
+ }
+ virtual ~CPacket()
+ { }
+
+ bool Send(int Socket, CAddress &Addr, unsigned int UID = XBMCClientUtils::GetUniqueIdentifier())
+ {
+ if (m_Payload.size() == 0)
+ ConstructPayload();
+ bool SendSuccessfull = true;
+ int NbrOfPackages = (m_Payload.size() / MAX_PAYLOAD_SIZE) + 1;
+ int Send = 0;
+ int Sent = 0;
+ int Left = m_Payload.size();
+ for (int Package = 1; Package <= NbrOfPackages; Package++)
+ {
+ if (Left > MAX_PAYLOAD_SIZE)
+ {
+ Send = MAX_PAYLOAD_SIZE;
+ Left -= Send;
+ }
+ else
+ {
+ Send = Left;
+ Left = 0;
+ }
+
+ ConstructHeader(m_PacketType, NbrOfPackages, Package, Send, UID, m_Header);
+ char t[MAX_PACKET_SIZE];
+ int i, j;
+ for (i = 0; i < 32; i++)
+ t[i] = m_Header[i];
+
+ for (j = 0; j < Send; j++)
+ t[(32 + j)] = m_Payload[j + Sent];
+
+ int rtn = sendto(Socket, t, (32 + Send), 0, Addr.GetAddress(), sizeof(struct sockaddr));
+
+ if (rtn != (32 + Send))
+ SendSuccessfull = false;
+
+ Sent += Send;
+ }
+ return SendSuccessfull;
+ }
+protected:
+ char m_Header[HEADER_SIZE];
+ unsigned short m_PacketType;
+
+ std::vector<char> m_Payload;
+
+ static void ConstructHeader(int PacketType, int NumberOfPackets, int CurrentPacket, unsigned short PayloadSize, unsigned int UniqueToken, char *Header)
+ {
+ sprintf(Header, "XBMC");
+ for (int i = 4; i < HEADER_SIZE; i++)
+ Header[i] = 0;
+ Header[4] = MAJOR_VERSION;
+ Header[5] = MINOR_VERSION;
+ if (CurrentPacket == 1)
+ {
+ Header[6] = ((PacketType & 0xff00) >> 8);
+ Header[7] = (PacketType & 0x00ff);
+ }
+ else
+ {
+ Header[6] = ((PT_BLOB & 0xff00) >> 8);
+ Header[7] = (PT_BLOB & 0x00ff);
+ }
+ Header[8] = ((CurrentPacket & 0xff000000) >> 24);
+ Header[9] = ((CurrentPacket & 0x00ff0000) >> 16);
+ Header[10] = ((CurrentPacket & 0x0000ff00) >> 8);
+ Header[11] = (CurrentPacket & 0x000000ff);
+
+ Header[12] = ((NumberOfPackets & 0xff000000) >> 24);
+ Header[13] = ((NumberOfPackets & 0x00ff0000) >> 16);
+ Header[14] = ((NumberOfPackets & 0x0000ff00) >> 8);
+ Header[15] = (NumberOfPackets & 0x000000ff);
+
+ Header[16] = ((PayloadSize & 0xff00) >> 8);
+ Header[17] = (PayloadSize & 0x00ff);
+
+ Header[18] = ((UniqueToken & 0xff000000) >> 24);
+ Header[19] = ((UniqueToken & 0x00ff0000) >> 16);
+ Header[20] = ((UniqueToken & 0x0000ff00) >> 8);
+ Header[21] = (UniqueToken & 0x000000ff);
+ }
+
+ virtual void ConstructPayload()
+ { }
+};
+
+class CPacketHELO : public CPacket
+{
+ /************************************************************************/
+ /* Payload format */
+ /* %s - device name (max 128 chars) */
+ /* %c - icontype ( 0=>NOICON, 1=>JPEG , 2=>PNG , 3=>GIF ) */
+ /* %s - my port ( 0=>not listening ) */
+ /* %d - reserved1 ( 0 ) */
+ /* %d - reserved2 ( 0 ) */
+ /* XX - imagedata ( can span multiple packets ) */
+ /************************************************************************/
+private:
+ std::vector<char> m_DeviceName;
+ unsigned short m_IconType;
+ char *m_IconData;
+ unsigned short m_IconSize;
+public:
+ virtual void ConstructPayload()
+ {
+ m_Payload.clear();
+
+ for (unsigned int i = 0; i < m_DeviceName.size(); i++)
+ m_Payload.push_back(m_DeviceName[i]);
+
+ m_Payload.push_back('\0');
+
+ m_Payload.push_back(m_IconType);
+
+ m_Payload.push_back(0);
+ m_Payload.push_back('\0');
+
+ for (int j = 0; j < 8; j++)
+ m_Payload.push_back(0);
+
+ for (int ico = 0; ico < m_IconSize; ico++)
+ m_Payload.push_back(m_IconData[ico]);
+ }
+
+ CPacketHELO(const char *DevName, unsigned short IconType, const char *IconFile = NULL) : CPacket()
+ {
+ m_PacketType = PT_HELO;
+
+ unsigned int len = strlen(DevName);
+ for (unsigned int i = 0; i < len; i++)
+ m_DeviceName.push_back(DevName[i]);
+
+ m_IconType = IconType;
+
+ if (IconType == ICON_NONE || IconFile == NULL)
+ {
+ m_IconData = NULL;
+ m_IconSize = 0;
+ return;
+ }
+
+ std::ifstream::pos_type size;
+
+ std::ifstream file (IconFile, std::ios::in|std::ios::binary|std::ios::ate);
+ if (file.is_open())
+ {
+ size = file.tellg();
+ m_IconData = new char [size];
+ file.seekg (0, std::ios::beg);
+ file.read (m_IconData, size);
+ file.close();
+ m_IconSize = size;
+ }
+ else
+ {
+ m_IconType = ICON_NONE;
+ m_IconSize = 0;
+ }
+ }
+
+ virtual ~CPacketHELO()
+ {
+ m_DeviceName.clear();
+ if (m_IconData)
+ free(m_IconData);
+ }
+};
+
+class CPacketNOTIFICATION : public CPacket
+{
+ /************************************************************************/
+ /* Payload format: */
+ /* %s - caption */
+ /* %s - message */
+ /* %c - icontype ( 0=>NOICON, 1=>JPEG , 2=>PNG , 3=>GIF ) */
+ /* %d - reserved ( 0 ) */
+ /* XX - imagedata ( can span multiple packets ) */
+ /************************************************************************/
+private:
+ std::vector<char> m_Title;
+ std::vector<char> m_Message;
+ unsigned short m_IconType;
+ char *m_IconData;
+ unsigned short m_IconSize;
+public:
+ virtual void ConstructPayload()
+ {
+ m_Payload.clear();
+
+ for (unsigned int i = 0; i < m_Title.size(); i++)
+ m_Payload.push_back(m_Title[i]);
+
+ m_Payload.push_back('\0');
+
+ for (unsigned int i = 0; i < m_Message.size(); i++)
+ m_Payload.push_back(m_Message[i]);
+
+ m_Payload.push_back('\0');
+
+ m_Payload.push_back(m_IconType);
+
+ for (int i = 0; i < 4; i++)
+ m_Payload.push_back(0);
+
+ for (int ico = 0; ico < m_IconSize; ico++)
+ m_Payload.push_back(m_IconData[ico]);
+ }
+
+ CPacketNOTIFICATION(const char *Title, const char *Message, unsigned short IconType, const char *IconFile = NULL) : CPacket()
+ {
+ m_PacketType = PT_NOTIFICATION;
+ m_IconData = NULL;
+
+ unsigned int len = 0;
+ if (Title != NULL)
+ {
+ len = strlen(Title);
+ for (unsigned int i = 0; i < len; i++)
+ m_Title.push_back(Title[i]);
+ }
+
+ if (Message != NULL)
+ {
+ len = strlen(Message);
+ for (unsigned int i = 0; i < len; i++)
+ m_Message.push_back(Message[i]);
+ }
+ m_IconType = IconType;
+
+ if (IconType == ICON_NONE || IconFile == NULL)
+ return;
+
+ std::ifstream::pos_type size;
+
+ std::ifstream file (IconFile, std::ios::in|std::ios::binary|std::ios::ate);
+ if (file.is_open())
+ {
+ size = file.tellg();
+ m_IconData = new char [size];
+ file.seekg (0, std::ios::beg);
+ file.read (m_IconData, size);
+ file.close();
+ m_IconSize = size;
+ }
+ else
+ {
+ m_IconType = ICON_NONE;
+ m_IconSize = 0;
+ }
+ }
+
+ virtual ~CPacketNOTIFICATION()
+ {
+ m_Title.clear();
+ m_Message.clear();
+ if (m_IconData)
+ free(m_IconData);
+ }
+};
+
+class CPacketBUTTON : public CPacket
+{
+ /************************************************************************/
+ /* Payload format */
+ /* %i - button code */
+ /* %i - flags 0x01 => use button map/name instead of code */
+ /* 0x02 => btn down */
+ /* 0x04 => btn up */
+ /* 0x08 => use amount */
+ /* 0x10 => queue event */
+ /* 0x20 => do not repeat */
+ /* 0x40 => virtual key */
+ /* 0x40 => axis key */
+ /* %i - amount ( 0 => 65k maps to -1 => 1 ) */
+ /* %s - device map (case sensitive and required if flags & 0x01) */
+ /* "KB" - Standard keyboard map */
+ /* "XG" - Xbox Gamepad */
+ /* "R1" - Xbox Remote */
+ /* "R2" - Xbox Universal Remote */
+ /* "LI:devicename" - valid LIRC device map where 'devicename' */
+ /* is the actual name of the LIRC device */
+ /* "JS<num>:joyname" - valid Joystick device map where */
+ /* 'joyname' is the name specified in */
+ /* the keymap. JS only supports button code */
+ /* and not button name currently (!0x01). */
+ /* %s - button name (required if flags & 0x01) */
+ /************************************************************************/
+private:
+ std::vector<char> m_DeviceMap;
+ std::vector<char> m_Button;
+ unsigned short m_ButtonCode;
+ unsigned short m_Amount;
+ unsigned short m_Flags;
+public:
+ virtual void ConstructPayload()
+ {
+ m_Payload.clear();
+
+ if (m_Button.size() != 0)
+ {
+ if (!(m_Flags & BTN_USE_NAME)) // If the BTN_USE_NAME isn't flagged for some reason
+ m_Flags |= BTN_USE_NAME;
+ m_ButtonCode = 0;
+ }
+ else
+ m_Button.clear();
+
+ if (m_Amount > 0)
+ {
+ if (!(m_Flags & BTN_USE_AMOUNT))
+ m_Flags |= BTN_USE_AMOUNT;
+ }
+ if (!((m_Flags & BTN_DOWN) || (m_Flags & BTN_UP))) //If none of them are tagged.
+ m_Flags |= BTN_DOWN;
+
+ m_Payload.push_back(((m_ButtonCode & 0xff00) >> 8));
+ m_Payload.push_back( (m_ButtonCode & 0x00ff));
+
+ m_Payload.push_back(((m_Flags & 0xff00) >> 8) );
+ m_Payload.push_back( (m_Flags & 0x00ff));
+
+ m_Payload.push_back(((m_Amount & 0xff00) >> 8) );
+ m_Payload.push_back( (m_Amount & 0x00ff));
+
+
+ for (unsigned int i = 0; i < m_DeviceMap.size(); i++)
+ m_Payload.push_back(m_DeviceMap[i]);
+
+ m_Payload.push_back('\0');
+
+ for (unsigned int i = 0; i < m_Button.size(); i++)
+ m_Payload.push_back(m_Button[i]);
+
+ m_Payload.push_back('\0');
+ }
+
+ CPacketBUTTON(const char *Button, const char *DeviceMap, unsigned short Flags, unsigned short Amount = 0) : CPacket()
+ {
+ m_PacketType = PT_BUTTON;
+ m_Flags = Flags;
+ m_ButtonCode = 0;
+ m_Amount = Amount;
+
+ unsigned int len = strlen(DeviceMap);
+ for (unsigned int i = 0; i < len; i++)
+ m_DeviceMap.push_back(DeviceMap[i]);
+
+ len = strlen(Button);
+ for (unsigned int i = 0; i < len; i++)
+ m_Button.push_back(Button[i]);
+ }
+
+ CPacketBUTTON(unsigned short ButtonCode, const char *DeviceMap, unsigned short Flags, unsigned short Amount = 0) : CPacket()
+ {
+ m_PacketType = PT_BUTTON;
+ m_Flags = Flags;
+ m_ButtonCode = ButtonCode;
+ m_Amount = Amount;
+
+ unsigned int len = strlen(DeviceMap);
+ for (unsigned int i = 0; i < len; i++)
+ m_DeviceMap.push_back(DeviceMap[i]);
+ }
+
+ CPacketBUTTON(unsigned short ButtonCode, unsigned short Flags, unsigned short Amount = 0) : CPacket()
+ {
+ m_PacketType = PT_BUTTON;
+ m_Flags = Flags;
+ m_ButtonCode = ButtonCode;
+ m_Amount = Amount;
+ }
+
+ // Used to send a release event
+ CPacketBUTTON() : CPacket()
+ {
+ m_PacketType = PT_BUTTON;
+ m_Flags = BTN_UP;
+ m_Amount = 0;
+ m_ButtonCode = 0;
+ }
+
+ virtual ~CPacketBUTTON()
+ {
+ m_DeviceMap.clear();
+ m_Button.clear();
+ }
+
+ inline unsigned short GetFlags() { return m_Flags; }
+ inline unsigned short GetButtonCode() { return m_ButtonCode; }
+};
+
+class CPacketPING : public CPacket
+{
+ /************************************************************************/
+ /* no payload */
+ /************************************************************************/
+public:
+ CPacketPING() : CPacket()
+ {
+ m_PacketType = PT_PING;
+ }
+ virtual ~CPacketPING()
+ { }
+};
+
+class CPacketBYE : public CPacket
+{
+ /************************************************************************/
+ /* no payload */
+ /************************************************************************/
+public:
+ CPacketBYE() : CPacket()
+ {
+ m_PacketType = PT_BYE;
+ }
+ virtual ~CPacketBYE()
+ { }
+};
+
+class CPacketMOUSE : public CPacket
+{
+ /************************************************************************/
+ /* Payload format */
+ /* %c - flags */
+ /* - 0x01 absolute position */
+ /* %i - mousex (0-65535 => maps to screen width) */
+ /* %i - mousey (0-65535 => maps to screen height) */
+ /************************************************************************/
+private:
+ unsigned short m_X;
+ unsigned short m_Y;
+ unsigned char m_Flag;
+public:
+ CPacketMOUSE(int X, int Y, unsigned char Flag = MS_ABSOLUTE)
+ {
+ m_PacketType = PT_MOUSE;
+ m_Flag = Flag;
+ m_X = X;
+ m_Y = Y;
+ }
+
+ virtual void ConstructPayload()
+ {
+ m_Payload.clear();
+
+ m_Payload.push_back(m_Flag);
+
+ m_Payload.push_back(((m_X & 0xff00) >> 8));
+ m_Payload.push_back( (m_X & 0x00ff));
+
+ m_Payload.push_back(((m_Y & 0xff00) >> 8));
+ m_Payload.push_back( (m_Y & 0x00ff));
+ }
+
+ virtual ~CPacketMOUSE()
+ { }
+};
+
+class CPacketLOG : public CPacket
+{
+ /************************************************************************/
+ /* Payload format */
+ /* %c - log type */
+ /* %s - message */
+ /************************************************************************/
+private:
+ std::vector<char> m_Message;
+ unsigned char m_LogLevel;
+ bool m_AutoPrintf;
+public:
+ CPacketLOG(int LogLevel, const char *Message, bool AutoPrintf = true)
+ {
+ m_PacketType = PT_LOG;
+
+ unsigned int len = strlen(Message);
+ for (unsigned int i = 0; i < len; i++)
+ m_Message.push_back(Message[i]);
+
+ m_LogLevel = LogLevel;
+ m_AutoPrintf = AutoPrintf;
+ }
+
+ virtual void ConstructPayload()
+ {
+ m_Payload.clear();
+
+ m_Payload.push_back( (m_LogLevel & 0x00ff) );
+
+ if (m_AutoPrintf)
+ {
+ char* str=&m_Message[0];
+ printf("%s\n", str);
+ }
+ for (unsigned int i = 0; i < m_Message.size(); i++)
+ m_Payload.push_back(m_Message[i]);
+
+ m_Payload.push_back('\0');
+ }
+
+ virtual ~CPacketLOG()
+ { }
+};
+
+class CPacketACTION : public CPacket
+{
+ /************************************************************************/
+ /* Payload format */
+ /* %c - action type */
+ /* %s - action message */
+ /************************************************************************/
+private:
+ unsigned char m_ActionType;
+ std::vector<char> m_Action;
+public:
+ CPacketACTION(const char *Action, unsigned char ActionType = ACTION_EXECBUILTIN)
+ {
+ m_PacketType = PT_ACTION;
+
+ m_ActionType = ActionType;
+ unsigned int len = strlen(Action);
+ for (unsigned int i = 0; i < len; i++)
+ m_Action.push_back(Action[i]);
+ }
+
+ virtual void ConstructPayload()
+ {
+ m_Payload.clear();
+
+ m_Payload.push_back(m_ActionType);
+ for (unsigned int i = 0; i < m_Action.size(); i++)
+ m_Payload.push_back(m_Action[i]);
+
+ m_Payload.push_back('\0');
+ }
+
+ virtual ~CPacketACTION()
+ { }
+};
+
+class CXBMCClient
+{
+private:
+ CAddress m_Addr;
+ int m_Socket;
+ unsigned int m_UID;
+public:
+ CXBMCClient(const char *IP = "127.0.0.1", int Port = 9777, int Socket = -1, unsigned int UID = 0)
+ {
+ m_Addr = CAddress(IP, Port);
+ if (Socket == -1)
+ m_Socket = socket(AF_INET, SOCK_DGRAM, 0);
+ else
+ m_Socket = Socket;
+
+ if (UID)
+ m_UID = UID;
+ else
+ m_UID = XBMCClientUtils::GetUniqueIdentifier();
+ }
+
+ void SendNOTIFICATION(const char *Title, const char *Message, unsigned short IconType, const char *IconFile = NULL)
+ {
+ if (m_Socket < 0)
+ return;
+
+ CPacketNOTIFICATION notification(Title, Message, IconType, IconFile);
+ notification.Send(m_Socket, m_Addr, m_UID);
+ }
+
+ void SendHELO(const char *DevName, unsigned short IconType, const char *IconFile = NULL)
+ {
+ if (m_Socket < 0)
+ return;
+
+ CPacketHELO helo(DevName, IconType, IconFile);
+ helo.Send(m_Socket, m_Addr, m_UID);
+ }
+
+ void SendButton(const char *Button, const char *DeviceMap, unsigned short Flags, unsigned short Amount = 0)
+ {
+ if (m_Socket < 0)
+ return;
+
+ CPacketBUTTON button(Button, DeviceMap, Flags, Amount);
+ button.Send(m_Socket, m_Addr, m_UID);
+ }
+
+ void SendButton(unsigned short ButtonCode, const char *DeviceMap, unsigned short Flags, unsigned short Amount = 0)
+ {
+ if (m_Socket < 0)
+ return;
+
+ CPacketBUTTON button(ButtonCode, DeviceMap, Flags, Amount);
+ button.Send(m_Socket, m_Addr, m_UID);
+ }
+
+ void SendButton(unsigned short ButtonCode, unsigned Flags, unsigned short Amount = 0)
+ {
+ if (m_Socket < 0)
+ return;
+
+ CPacketBUTTON button(ButtonCode, Flags, Amount);
+ button.Send(m_Socket, m_Addr, m_UID);
+ }
+
+ void SendMOUSE(int X, int Y, unsigned char Flag = MS_ABSOLUTE)
+ {
+ if (m_Socket < 0)
+ return;
+
+ CPacketMOUSE mouse(X, Y, Flag);
+ mouse.Send(m_Socket, m_Addr, m_UID);
+ }
+
+ void SendLOG(int LogLevel, const char *Message, bool AutoPrintf = true)
+ {
+ if (m_Socket < 0)
+ return;
+
+ CPacketLOG log(LogLevel, Message, AutoPrintf);
+ log.Send(m_Socket, m_Addr, m_UID);
+ }
+
+ void SendACTION(const char *ActionMessage, int ActionType = ACTION_EXECBUILTIN)
+ {
+ if (m_Socket < 0)
+ return;
+
+ CPacketACTION action(ActionMessage, ActionType);
+ action.Send(m_Socket, m_Addr, m_UID);
+ }
+};
+
+#endif
diff --git a/tools/EventClients/lib/java/build.xml b/tools/EventClients/lib/java/build.xml
new file mode 100755
index 0000000000..fe25bde90a
--- /dev/null
+++ b/tools/EventClients/lib/java/build.xml
@@ -0,0 +1,72 @@
+<project default="jar" name="JXBMCEventClient">
+ <description description="JXBMCEventClient"/>
+ <target name="init">
+ <tstamp/>
+ <property name="srcdir" value="${basedir}/src"/>
+ <property name="classdir" value="${basedir}/classes"/>
+ <property name="apidir" value="${basedir}/doc/api"/>
+ <property name="libdir" value="/usr/local/share/java/classes"/>
+ <property name="projectname" value="JXBMCEventClient"/>
+ <property name="jarfile" value="${projectname}.jar"/>
+ <property name="distdir" value="${basedir}/../dist"/>
+ <fileset id="eventclient.files" dir="${classdir}" includes="**/*"/>
+ <path id="classpath">
+ <pathelement location="${libdir}/junit.jar" />
+ </path>
+ </target>
+
+
+ <target name="jar" depends="compile">
+ <jar jarfile="${jarfile}" index="true">
+ <fileset refid="eventclient.files"/>
+ </jar>
+ </target>
+
+ <target name="dist" depends="jar, javadoc">
+ <fail unless="version">use: ant dist -Dversion=x.x</fail>
+ <mkdir dir="${distdir}"/>
+ <copy todir="${distdir}/doc">
+ <fileset dir="${apidir}"/>
+ </copy>
+ <property name="filename" value="${projectname}-${version}"/>
+ <copy file="${jarfile}" tofile="${distdir}/${projectname}-${version}.jar" />
+ <tar destfile="${distdir}/${filename}.tar.gz" compression="gzip">
+ <tarfileset dir="${basedir}" prefix="${filename}">
+ <exclude name="classes/**"/>
+ <exclude name="**/CVS"/>
+ <exclude name="**/.*"/>
+ <exclude name="${jarfile}"/>
+ <exclude name="doc/**"/>
+ </tarfileset>
+ </tar>
+ </target>
+
+ <target name="compile" depends="init" description="Compiles all classes">
+ <mkdir dir="${classdir}"/>
+ <javac debug="yes" deprecation="true" destdir="${classdir}" srcdir="${srcdir}" classpathref="classpath"/>
+ </target>
+
+ <target name="javadoc" depends="init">
+ <mkdir dir="${apidir}"/>
+ <javadoc
+ packagenames="org.xbmc.eventclient.*"
+ sourcepath="${srcdir}"
+ defaultexcludes="yes"
+ destdir="${apidir}"
+ author="true"
+ version="true"
+ use="true"
+ private="false"
+ windowtitle="${projectname} API"
+ classpathref="classpath">
+ <link href="http://java.sun.com/j2se/1.4.2/docs/api/"/>
+ </javadoc>
+ </target>
+
+ <target name="clean" depends="init" description="cleans all classes and jars">
+ <delete dir="${classdir}"/>
+ <delete dir="${apidir}"/>
+ <delete file="${jarfile}"/>
+ </target>
+
+</project>
diff --git a/tools/EventClients/lib/java/src/org/xbmc/eventclient/Packet.java b/tools/EventClients/lib/java/src/org/xbmc/eventclient/Packet.java
new file mode 100755
index 0000000000..4904d5c9b1
--- /dev/null
+++ b/tools/EventClients/lib/java/src/org/xbmc/eventclient/Packet.java
@@ -0,0 +1,271 @@
+package org.xbmc.eventclient;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+
+/**
+ * XBMC Event Client Class
+ * <p>
+ * Implementation of XBMC's UDP based input system.
+ * A set of classes that abstract the various packets that the event server
+ * currently supports. In addition, there's also a class, XBMCClient, that
+ * provides functions that sends the various packets. Use XBMCClient if you
+ * don't need complete control over packet structure.
+ * </p>
+ * <p>
+ * The basic workflow involves:
+ * <ol>
+ * <li>Send a HELO packet</li>
+ * <li>Send x number of valid packets</li>
+ * <li>Send a BYE packet</li>
+ * </ol>
+ * </p>
+ * <p>
+ * IMPORTANT NOTE ABOUT TIMEOUTS:
+ * A client is considered to be timed out if XBMC doesn't received a packet
+ * at least once every 60 seconds. To "ping" XBMC with an empty packet use
+ * PacketPING or XBMCClient.ping(). See the documentation for details.
+ * </p>
+ * <p>
+ * Base class that implements a single event packet.
+ * - Generic packet structure (maximum 1024 bytes per packet)
+ * - Header is 32 bytes long, so 992 bytes available for payload
+ * - large payloads can be split into multiple packets using H4 and H5
+ * H5 should contain total no. of packets in such a case
+ * - H6 contains length of P1, which is limited to 992 bytes
+ * - if H5 is 0 or 1, then H4 will be ignored (single packet msg)
+ * - H7 must be set to zeros for now
+ * </p>
+ * <pre>
+ * -----------------------------
+ * | -H1 Signature ("XBMC") | - 4 x CHAR 4B
+ * | -H2 Version (eg. 2.0) | - 2 x UNSIGNED CHAR 2B
+ * | -H3 PacketType | - 1 x UNSIGNED SHORT 2B
+ * | -H4 Sequence number | - 1 x UNSIGNED LONG 4B
+ * | -H5 No. of packets in msg | - 1 x UNSIGNED LONG 4B
+ * | -H6 Payloadsize of packet | - 1 x UNSIGNED SHORT 2B
+ * | -H7 Client's unique token | - 1 x UNSIGNED LONG 4B
+ * | -H8 Reserved | - 10 x UNSIGNED CHAR 10B
+ * |---------------------------|
+ * | -P1 payload | -
+ * -----------------------------
+ * </pre>
+ * @author Stefan Agner
+ *
+ */
+public abstract class Packet {
+
+ private byte[] sig;
+ private byte[] payload = new byte[0];
+ private byte minver;
+ private byte majver;
+
+ private short packettype;
+
+
+ private final static short MAX_PACKET_SIZE = 1024;
+ private final static short HEADER_SIZE = 32;
+ private final static short MAX_PAYLOAD_SIZE = MAX_PACKET_SIZE - HEADER_SIZE;
+
+ protected final static byte PT_HELO = 0x01;
+ protected final static byte PT_BYE = 0x02;
+ protected final static byte PT_BUTTON = 0x03;
+ protected final static byte PT_MOUSE = 0x04;
+ protected final static byte PT_PING = 0x05;
+ protected final static byte PT_BROADCAST = 0x06;
+ protected final static byte PT_NOTIFICATION = 0x07;
+ protected final static byte PT_BLOB = 0x08;
+ protected final static byte PT_LOG = 0x09;
+ protected final static byte PT_ACTION = 0x0A;
+ protected final static byte PT_DEBUG = (byte)0xFF;
+
+ public final static byte ICON_NONE = 0x00;
+ public final static byte ICON_JPEG = 0x01;
+ public final static byte ICON_PNG = 0x02;
+ public final static byte ICON_GIF = 0x03;
+
+ private static int uid = (int)(Math.random()*Integer.MAX_VALUE);
+
+ /**
+ * This is an Abstract class and cannot be instanced. Please use one of the Packet implementation Classes
+ * (PacketXXX).
+ *
+ * Implements an XBMC Event Client Packet. Type is to be specified at creation time, Payload can be added
+ * with the various appendPayload methods. Packet can be sent through UDP-Socket with method "send".
+ * @param packettype Type of Packet (PT_XXX)
+ */
+ protected Packet(short packettype)
+ {
+ sig = new byte[] {'X', 'B', 'M', 'C' };
+ minver = 0;
+ majver = 2;
+ this.packettype = packettype;
+ }
+
+ /**
+ * Appends a String to the payload (terminated with 0x00)
+ * @param payload Payload as String
+ */
+ protected void appendPayload(String payload)
+ {
+ byte[] payloadarr = payload.getBytes();
+ int oldpayloadsize = this.payload.length;
+ byte[] oldpayload = this.payload;
+ this.payload = new byte[oldpayloadsize+payloadarr.length+1]; // Create new Array with more place (+1 for string terminator)
+ System.arraycopy(oldpayload, 0, this.payload, 0, oldpayloadsize);
+ System.arraycopy(payloadarr, 0, this.payload, oldpayloadsize, payloadarr.length);
+ }
+
+ /**
+ * Appends a single Byte to the payload
+ * @param payload Payload
+ */
+ protected void appendPayload(byte payload)
+ {
+ appendPayload(new byte[] { payload });
+ }
+
+ /**
+ * Appends a Byte-Array to the payload
+ * @param payloadarr Payload
+ */
+ protected void appendPayload(byte[] payloadarr)
+ {
+ int oldpayloadsize = this.payload.length;
+ byte[] oldpayload = this.payload;
+ this.payload = new byte[oldpayloadsize+payloadarr.length];
+ System.arraycopy(oldpayload, 0, this.payload, 0, oldpayloadsize);
+ System.arraycopy(payloadarr, 0, this.payload, oldpayloadsize, payloadarr.length);
+ }
+
+ /**
+ * Appends an integer to the payload
+ * @param i Payload
+ */
+ protected void appendPayload(int i) {
+ appendPayload(intToByteArray(i));
+ }
+
+ /**
+ * Appends a short to the payload
+ * @param s Payload
+ */
+ protected void appendPayload(short s) {
+ appendPayload(shortToByteArray(s));
+ }
+
+ /**
+ * Get Number of Packets which will be sent with current Payload...
+ * @return Number of Packets
+ */
+ public int getNumPackets()
+ {
+ return (int)((payload.length + (MAX_PAYLOAD_SIZE - 1)) / MAX_PAYLOAD_SIZE);
+ }
+
+ /**
+ * Get Header for a specific Packet in this sequence...
+ * @param seq Current sequence number
+ * @param maxseq Maximal sequence number
+ * @param actpayloadsize Payloadsize of this packet
+ * @return Byte-Array with Header information (currently 32-Byte long, see HEADER_SIZE)
+ */
+ private byte[] getHeader(int seq, int maxseq, short actpayloadsize)
+ {
+ byte[] header = new byte[HEADER_SIZE];
+ System.arraycopy(sig, 0, header, 0, 4);
+ header[4] = majver;
+ header[5] = minver;
+ byte[] packettypearr = shortToByteArray(this.packettype);
+ System.arraycopy(packettypearr, 0, header, 6, 2);
+ byte[] seqarr = intToByteArray(seq);
+ System.arraycopy(seqarr, 0, header, 8, 4);
+ byte[] maxseqarr = intToByteArray(maxseq);
+ System.arraycopy(maxseqarr, 0, header, 12, 4);
+ byte[] payloadsize = shortToByteArray(actpayloadsize);
+ System.arraycopy(payloadsize, 0, header, 16, 2);
+ byte[] uid = intToByteArray(Packet.uid);
+ System.arraycopy(uid, 0, header, 18, 4);
+ byte[] reserved = new byte[10];
+ System.arraycopy(reserved, 0, header, 22, 10);
+
+ return header;
+ }
+
+ /**
+ * Generates the whole UDP-Message with Header and Payload of a specific Packet in sequence
+ * @param seq Current sequence number
+ * @return Byte-Array with UDP-Message
+ */
+ private byte[] getUDPMessage(int seq)
+ {
+ int maxseq = (int)((payload.length + (MAX_PAYLOAD_SIZE - 1)) / MAX_PAYLOAD_SIZE);
+ if(seq > maxseq)
+ return null;
+
+ short actpayloadsize;
+
+ if(seq == maxseq)
+ actpayloadsize = (short)(payload.length%MAX_PAYLOAD_SIZE);
+
+ else
+ actpayloadsize = (short)MAX_PAYLOAD_SIZE;
+
+ byte[] pack = new byte[HEADER_SIZE+actpayloadsize];
+
+ System.arraycopy(getHeader(seq, maxseq, actpayloadsize), 0, pack, 0, HEADER_SIZE);
+ System.arraycopy(payload, (seq-1)*MAX_PAYLOAD_SIZE, pack, HEADER_SIZE, actpayloadsize);
+
+ return pack;
+ }
+
+ /**
+ * Sends this packet to the EventServer
+ * @param adr Address of the EventServer
+ * @param port Port of the EventServer
+ * @throws IOException
+ */
+ public void send(InetAddress adr, int port) throws IOException
+ {
+ int maxseq = getNumPackets();
+ DatagramSocket s = new DatagramSocket();
+
+ // For each Packet in Sequence...
+ for(int seq=1;seq<=maxseq;seq++)
+ {
+ // Get Message and send them...
+ byte[] pack = getUDPMessage(seq);
+ DatagramPacket p = new DatagramPacket(pack, pack.length);
+ p.setAddress(adr);
+ p.setPort(port);
+ s.send(p);
+ }
+ }
+
+ /**
+ * Helper Method to convert an integer to a Byte array
+ * @param value
+ * @return Byte-Array
+ */
+ private static final byte[] intToByteArray(int value) {
+ return new byte[] {
+ (byte)(value >>> 24),
+ (byte)(value >>> 16),
+ (byte)(value >>> 8),
+ (byte)value};
+ }
+
+ /**
+ * Helper Method to convert an short to a Byte array
+ * @param value
+ * @return Byte-Array
+ */
+ private static final byte[] shortToByteArray(short value) {
+ return new byte[] {
+ (byte)(value >>> 8),
+ (byte)value};
+ }
+
+
+}
diff --git a/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketACTION.java b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketACTION.java
new file mode 100755
index 0000000000..ae633c783a
--- /dev/null
+++ b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketACTION.java
@@ -0,0 +1,43 @@
+package org.xbmc.eventclient;
+/**
+ * XBMC Event Client Class
+ *
+ * An ACTION packet tells XBMC to do the action specified, based on the type it knows were it needs to be sent.
+ * The idea is that this will be as in scripting/skining and keymapping, just triggered from afar.
+ * @author Stefan Agner
+ *
+ */
+public class PacketACTION extends Packet {
+
+ public final static byte ACTION_EXECBUILTIN = 0x01;
+ public final static byte ACTION_BUTTON = 0x02;
+
+
+ /**
+ * An ACTION packet tells XBMC to do the action specified, based on the type it knows were it needs to be sent.
+ * @param actionmessage Actionmessage (as in scripting/skinning)
+ */
+ public PacketACTION(String actionmessage)
+ {
+ super(PT_ACTION);
+ byte actiontype = ACTION_EXECBUILTIN;
+ appendPayload(actionmessage, actiontype);
+ }
+
+ /**
+ * An ACTION packet tells XBMC to do the action specified, based on the type it knows were it needs to be sent.
+ * @param actionmessage Actionmessage (as in scripting/skinning)
+ * @param actiontype Actiontype (ACTION_EXECBUILTIN or ACTION_BUTTON)
+ */
+ public PacketACTION(String actionmessage, byte actiontype)
+ {
+ super(PT_ACTION);
+ appendPayload(actionmessage, actiontype);
+ }
+
+ private void appendPayload(String actionmessage, byte actiontype)
+ {
+ appendPayload(actiontype);
+ appendPayload(actionmessage);
+ }
+}
diff --git a/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketBUTTON.java b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketBUTTON.java
new file mode 100755
index 0000000000..cc0ff35efb
--- /dev/null
+++ b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketBUTTON.java
@@ -0,0 +1,139 @@
+package org.xbmc.eventclient;
+/**
+ * XBMC Event Client Class
+ *
+ * A button packet send a key press or release event to XBMC
+ * @author Stefan Agner
+ *
+ */
+public class PacketBUTTON extends Packet {
+
+ protected final static byte BT_USE_NAME = 0x01;
+ protected final static byte BT_DOWN = 0x02;
+ protected final static byte BT_UP = 0x04;
+ protected final static byte BT_USE_AMOUNT = 0x08;
+ protected final static byte BT_QUEUE = 0x10;
+ protected final static byte BT_NO_REPEAT = 0x20;
+ protected final static byte BT_VKEY = 0x40;
+ protected final static byte BT_AXIS = (byte)0x80;
+ protected final static byte BT_AXISSINGLE = (byte)0x100;
+
+ /**
+ * A button packet send a key press or release event to XBMC
+ * @param code raw button code (default: 0)
+ * @param repeat this key press should repeat until released (default: 1)
+ * Note that queued pressed cannot repeat.
+ * @param down if this is 1, it implies a press event, 0 implies a release
+ * event. (default: 1)
+ * @param queue a queued key press means that the button event is
+ * executed just once after which the next key press is processed.
+ * It can be used for macros. Currently there is no support for
+ * time delays between queued presses. (default: 0)
+ * @param amount unimplemented for now; in the future it will be used for
+ * specifying magnitude of analog key press events
+ * @param axis
+ */
+ public PacketBUTTON(short code, boolean repeat, boolean down, boolean queue, short amount, byte axis)
+ {
+ super(PT_BUTTON);
+ String map_name = "";
+ String button_name = "";
+ short flags = 0;
+ appendPayload(code, map_name, button_name, repeat, down, queue, amount, axis, flags);
+ }
+
+ /**
+ * A button packet send a key press or release event to XBMC
+ * @param map_name a combination of map_name and button_name refers to a
+ * mapping in the user's Keymap.xml or Lircmap.xml.
+ * map_name can be one of the following:
+ * <ul>
+ * <li>"KB" => standard keyboard map ( <keyboard> section )</li>
+ * <li>"XG" => xbox gamepad map ( <gamepad> section )</li>
+ * <li>"R1" => xbox remote map ( <remote> section )</li>
+ * <li>"R2" => xbox universal remote map ( <universalremote> section )</li>
+ * <li>"LI:devicename" => LIRC remote map where 'devicename' is the
+ * actual device's name</li></ul>
+ * @param button_name a button name defined in the map specified in map_name.
+ * For example, if map_name is "KB" refering to the <keyboard> section in Keymap.xml
+ * then, valid button_names include "printscreen", "minus", "x", etc.
+ * @param repeat this key press should repeat until released (default: 1)
+ * Note that queued pressed cannot repeat.
+ * @param down if this is 1, it implies a press event, 0 implies a release
+ * event. (default: 1)
+ * @param queue a queued key press means that the button event is
+ * executed just once after which the next key press is processed.
+ * It can be used for macros. Currently there is no support for
+ * time delays between queued presses. (default: 0)
+ * @param amount unimplemented for now; in the future it will be used for
+ * specifying magnitude of analog key press events
+ * @param axis
+ */
+ public PacketBUTTON(String map_name, String button_name, boolean repeat, boolean down, boolean queue, short amount, byte axis)
+ {
+ super(PT_BUTTON);
+ short code = 0;
+ short flags = BT_USE_NAME;
+ appendPayload(code, map_name, button_name, repeat, down, queue, amount, axis, flags);
+ }
+
+ /**
+ * Appends Payload for a Button Packet (this method is used by the different Constructors of this Packet)
+ * @param code raw button code (default: 0)
+ * @param map_name a combination of map_name and button_name refers to a
+ * mapping in the user's Keymap.xml or Lircmap.xml.
+ * map_name can be one of the following:
+ * <ul>
+ * <li>"KB" => standard keyboard map ( <keyboard> section )</li>
+ * <li>"XG" => xbox gamepad map ( <gamepad> section )</li>
+ * <li>"R1" => xbox remote map ( <remote> section )</li>
+ * <li>"R2" => xbox universal remote map ( <universalremote> section )</li>
+ * <li>"LI:devicename" => LIRC remote map where 'devicename' is the
+ * actual device's name</li></ul>
+ * @param button_name a button name defined in the map specified in map_name.
+ * For example, if map_name is "KB" refering to the <keyboard> section in Keymap.xml
+ * then, valid button_names include "printscreen", "minus", "x", etc.
+ * @param repeat this key press should repeat until released (default: 1)
+ * Note that queued pressed cannot repeat.
+ * @param down if this is 1, it implies a press event, 0 implies a release
+ * event. (default: 1)
+ * @param queue a queued key press means that the button event is
+ * executed just once after which the next key press is processed.
+ * It can be used for macros. Currently there is no support for
+ * time delays between queued presses. (default: 0)
+ * @param amount unimplemented for now; in the future it will be used for
+ * specifying magnitude of analog key press events
+ * @param axis
+ * @param flags Packet specific flags
+ */
+ private void appendPayload(short code, String map_name, String button_name, boolean repeat, boolean down, boolean queue, short amount, byte axis, short flags)
+ {
+ if(amount>0)
+ flags |= BT_USE_AMOUNT;
+ else
+ amount = 0;
+
+ if(down)
+ flags |= BT_DOWN;
+ else
+ flags |= BT_UP;
+
+ if(!repeat)
+ flags |= BT_NO_REPEAT;
+
+ if(queue)
+ flags |= BT_QUEUE;
+
+ if(axis == 1)
+ flags |= BT_AXISSINGLE;
+ else if (axis == 2)
+ flags |= BT_AXIS;
+
+
+ appendPayload(code);
+ appendPayload(flags);
+ appendPayload(amount);
+ appendPayload(map_name);
+ appendPayload(button_name);
+ }
+}
diff --git a/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketBYE.java b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketBYE.java
new file mode 100755
index 0000000000..8c831b1ac0
--- /dev/null
+++ b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketBYE.java
@@ -0,0 +1,19 @@
+package org.xbmc.eventclient;
+/**
+ * XBMC Event Client Class
+ *
+ * A BYE packet terminates the connection to XBMC.
+ * @author Stefan Agner
+ *
+ */
+public class PacketBYE extends Packet
+{
+
+ /**
+ * A BYE packet terminates the connection to XBMC.
+ */
+ public PacketBYE()
+ {
+ super(PT_BYE);
+ }
+}
diff --git a/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketHELO.java b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketHELO.java
new file mode 100755
index 0000000000..f68ba46421
--- /dev/null
+++ b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketHELO.java
@@ -0,0 +1,46 @@
+package org.xbmc.eventclient;
+import java.nio.charset.Charset;
+
+/**
+ * XBMC Event Client Class
+ *
+ * A HELO packet establishes a valid connection to XBMC. It is the
+ * first packet that should be sent.
+ * @author Stefan Agner
+ *
+ */
+public class PacketHELO extends Packet {
+
+
+ /**
+ * A HELO packet establishes a valid connection to XBMC.
+ * @param devicename Name of the device which connects to XBMC
+ */
+ public PacketHELO(String devicename)
+ {
+ super(PT_HELO);
+ this.appendPayload(devicename);
+ this.appendPayload(ICON_NONE);
+ this.appendPayload((short)0); // port no
+ this.appendPayload(0); // reserved1
+ this.appendPayload(0); // reserved2
+ }
+
+ /**
+ * A HELO packet establishes a valid connection to XBMC.
+ * @param devicename Name of the device which connects to XBMC
+ * @param iconType Type of the icon (Packet.ICON_PNG, Packet.ICON_JPEG or Packet.ICON_GIF)
+ * @param iconData The icon as a Byte-Array
+ */
+ public PacketHELO(String devicename, byte iconType, byte[] iconData)
+ {
+ super(PT_HELO);
+ this.appendPayload(devicename);
+ this.appendPayload(iconType);
+ this.appendPayload((short)0); // port no
+ this.appendPayload(0); // reserved1
+ this.appendPayload(0); // reserved2
+ this.appendPayload(iconData); // reserved2
+ }
+
+}
diff --git a/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketLOG.java b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketLOG.java
new file mode 100755
index 0000000000..693ad71ea6
--- /dev/null
+++ b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketLOG.java
@@ -0,0 +1,30 @@
+package org.xbmc.eventclient;
+/**
+ * XBMC Event Client Class
+ *
+ * A LOG packet tells XBMC to log the message to xbmc.log with the loglevel as specified.
+ * @author Stefan Agner
+ *
+ */
+public class PacketLOG extends Packet {
+
+ /**
+ * A LOG packet tells XBMC to log the message to xbmc.log with the loglevel as specified.
+ * @param loglevel the loglevel, follows XBMC standard.
+ * <ul>
+ * <li>0 = DEBUG</li>
+ * <li>1 = INFO</li>
+ * <li>2 = NOTICE</li>
+ * <li>3 = WARNING</li>
+ * <li>4 = ERROR</li>
+ * <li>5 = SEVERE</li>
+ * </ul>
+ * @param logmessage the message to log
+ */
+ public PacketLOG(byte loglevel, String logmessage)
+ {
+ super(PT_LOG);
+ appendPayload(loglevel);
+ appendPayload(logmessage);
+ }
+}
diff --git a/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketMOUSE.java b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketMOUSE.java
new file mode 100755
index 0000000000..d7755ef0cc
--- /dev/null
+++ b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketMOUSE.java
@@ -0,0 +1,27 @@
+package org.xbmc.eventclient;
+/**
+ * XBMC Event Client Class
+ *
+ * A MOUSE packets sets the mouse position in XBMC
+ * @author Stefan Agner
+ *
+ */
+public class PacketMOUSE extends Packet {
+
+ protected final static byte MS_ABSOLUTE = 0x01;
+
+ /**
+ * A MOUSE packets sets the mouse position in XBMC
+ * @param x horitontal position ranging from 0 to 65535
+ * @param y vertical position ranging from 0 to 65535
+ */
+ public PacketMOUSE(int x, int y)
+ {
+ super(PT_MOUSE);
+ byte flags = 0;
+ flags |= MS_ABSOLUTE;
+ appendPayload(flags);
+ appendPayload((short)x);
+ appendPayload((short)y);
+ }
+}
diff --git a/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketNOTIFICATION.java b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketNOTIFICATION.java
new file mode 100755
index 0000000000..15a5ec8f27
--- /dev/null
+++ b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketNOTIFICATION.java
@@ -0,0 +1,53 @@
+package org.xbmc.eventclient;
+/**
+ * XBMC Event Client Class
+ *
+ * This packet displays a notification window in XBMC. It can contain
+ * a caption, a message and an icon.
+ * @author Stefan Agner
+ *
+ */
+public class PacketNOTIFICATION extends Packet {
+
+ /**
+ * This packet displays a notification window in XBMC.
+ * @param title Message title
+ * @param message The actual message
+ * @param iconType Type of the icon (Packet.ICON_PNG, Packet.ICON_JPEG or Packet.ICON_GIF)
+ * @param iconData The icon as a Byte-Array
+ */
+ public PacketNOTIFICATION(String title, String message, byte iconType, byte[] iconData)
+ {
+ super(PT_NOTIFICATION);
+ appendPayload(title, message, iconType, iconData);
+ }
+
+ /**
+ * This packet displays a notification window in XBMC.
+ * @param title Message title
+ * @param message The actual message
+ */
+ public PacketNOTIFICATION(String title, String message)
+ {
+ super(PT_NOTIFICATION);
+ appendPayload(title, message, Packet.ICON_NONE, null);
+ }
+
+ /**
+ * Appends the payload to the packet...
+ * @param title Message title
+ * @param message The actual message
+ * @param iconType Type of the icon (Packet.ICON_PNG, Packet.ICON_JPEG or Packet.ICON_GIF)
+ * @param iconData The icon as a Byte-Array
+ */
+ private void appendPayload(String title, String message, byte iconType, byte[] iconData)
+ {
+ appendPayload(title);
+ appendPayload(message);
+ appendPayload(iconType);
+ appendPayload(0); // reserved
+ if(iconData!=null)
+ appendPayload(iconData);
+
+ }
+}
diff --git a/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketPING.java b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketPING.java
new file mode 100755
index 0000000000..e405812f32
--- /dev/null
+++ b/tools/EventClients/lib/java/src/org/xbmc/eventclient/PacketPING.java
@@ -0,0 +1,19 @@
+package org.xbmc.eventclient;
+/**
+ * XBMC Event Client Class
+ *
+ * A PING packet tells XBMC that the client is still alive. All valid
+ * packets act as ping (not just this one). A client needs to ping
+ * XBMC at least once in 60 seconds or it will time
+ * @author Stefan Agner
+ *
+ */
+public class PacketPING extends Packet {
+ /**
+ * A PING packet tells XBMC that the client is still alive.
+ */
+ public PacketPING()
+ {
+ super(PT_PING);
+ }
+}
diff --git a/tools/EventClients/lib/java/src/org/xbmc/eventclient/XBMCClient.java b/tools/EventClients/lib/java/src/org/xbmc/eventclient/XBMCClient.java
new file mode 100755
index 0000000000..4a0f85fd02
--- /dev/null
+++ b/tools/EventClients/lib/java/src/org/xbmc/eventclient/XBMCClient.java
@@ -0,0 +1,300 @@
+package org.xbmc.eventclient;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+
+/**
+ * XBMC Event Client Class
+ *
+ * Implements an XBMC-Client. This class can be used to implement your own application which
+ * should act as a Input device for XBMC. Also starts a Ping-Thread, which tells the XBMC EventServer
+ * that the client is alive. Therefore if you close your application you SHOULD call stopClient()!
+ * @author Stefan Agner
+ *
+ */
+public class XBMCClient
+{
+ private boolean hasIcon = false;
+ private String deviceName;
+ private PingThread oPingThread;
+ private byte iconType = Packet.ICON_PNG;
+ private byte[] iconData;
+ private InetAddress hostAddress;
+ private int hostPort;
+
+ /**
+ * Starts a XBMC EventClient.
+ * @param hostAddress Address of the Host running XBMC
+ * @param hostPort Port of the Host running XBMC (default 9777)
+ * @param deviceName Name of the Device
+ * @param iconFile Path to the Iconfile (PNG, JPEG or GIF)
+ * @throws IOException
+ */
+ public XBMCClient(InetAddress hostAddress, int hostPort, String deviceName, String iconFile) throws IOException
+ {
+ byte iconType = Packet.ICON_PNG;
+ // Assume png as icon type
+ if(iconFile.toLowerCase().endsWith(".jpeg"))
+ iconType = Packet.ICON_JPEG;
+ if(iconFile.toLowerCase().endsWith(".jpg"))
+ iconType = Packet.ICON_JPEG;
+ if(iconFile.toLowerCase().endsWith(".gif"))
+ iconType = Packet.ICON_GIF;
+
+ // Read the icon file to the byte array...
+ FileInputStream iconFileStream = new FileInputStream(iconFile);
+ byte[] iconData = new byte[iconFileStream.available()];
+ iconFileStream.read(iconData);
+
+ hasIcon = true;
+
+ // Call start-Method...
+ startClient(hostAddress, hostPort, deviceName, iconType, iconData);
+ }
+
+
+ /**
+ * Starts a XBMC EventClient.
+ * @param hostAddress Address of the Host running XBMC
+ * @param hostPort Port of the Host running XBMC (default 9777)
+ * @param deviceName Name of the Device
+ * @param iconType Type of the icon file (see Packet.ICON_PNG, Packet.ICON_JPEG or Packet.ICON_GIF)
+ * @param iconData The icon itself as a Byte-Array
+ * @throws IOException
+ */
+ public XBMCClient(InetAddress hostAddress, int hostPort, String deviceName, byte iconType, byte[] iconData) throws IOException
+ {
+ hasIcon = true;
+ startClient(hostAddress, hostPort, deviceName, iconType, iconData);
+ }
+
+ /**
+ * Starts a XBMC EventClient without an icon.
+ * @param hostAddress Address of the Host running XBMC
+ * @param hostPort Port of the Host running XBMC (default 9777)
+ * @param deviceName Name of the Device
+ * @throws IOException
+ */
+ public XBMCClient(InetAddress hostAddress, int hostPort, String deviceName) throws IOException
+ {
+ hasIcon = false;
+ byte iconType = Packet.ICON_NONE;
+ byte[] iconData = null;
+ startClient(hostAddress, hostPort, deviceName, iconType, iconData);
+ }
+
+
+ /**
+ * Starts a XBMC EventClient.
+ * @param hostAddress Address of the Host running XBMC
+ * @param hostPort Port of the Host running XBMC (default 9777)
+ * @param deviceName Name of the Device
+ * @param iconType Type of the icon file (see Packet.ICON_PNG, Packet.ICON_JPEG or Packet.ICON_GIF)
+ * @param iconData The icon itself as a Byte-Array
+ * @throws IOException
+ */
+ private void startClient(InetAddress hostAddress, int hostPort, String deviceName, byte iconType, byte[] iconData) throws IOException
+ {
+ // Save host address and port
+ this.hostAddress = hostAddress;
+ this.hostPort = hostPort;
+ this.deviceName = deviceName;
+
+ this.iconType = iconType;
+ this.iconData = iconData;
+
+ // Send Hello Packet...
+ PacketHELO p;
+ if(hasIcon)
+ p = new PacketHELO(deviceName, iconType, iconData);
+ else
+ p = new PacketHELO(deviceName);
+
+ p.send(hostAddress, hostPort);
+
+ // Start Thread (for Ping packets...)
+ oPingThread = new PingThread(hostAddress, hostPort, 20000);
+ oPingThread.start();
+ }
+
+ /**
+ * Stops the XBMC EventClient (especially the Ping-Thread)
+ * @throws IOException
+ */
+ public void stopClient() throws IOException
+ {
+ // Stop Ping-Thread...
+ oPingThread.giveup();
+ oPingThread.interrupt();
+
+ PacketBYE p = new PacketBYE();
+ p.send(hostAddress, hostPort);
+ }
+
+
+ /**
+ * Displays a notification window in XBMC.
+ * @param title Message title
+ * @param message The actual message
+ */
+ public void sendNotification(String title, String message) throws IOException
+ {
+ PacketNOTIFICATION p;
+ if(hasIcon)
+ p = new PacketNOTIFICATION(title, message, iconType, iconData);
+ else
+ p = new PacketNOTIFICATION(title, message);
+ p.send(hostAddress, hostPort);
+ }
+
+ /**
+ * Sends a Button event
+ * @param code raw button code (default: 0)
+ * @param repeat this key press should repeat until released (default: 1)
+ * Note that queued pressed cannot repeat.
+ * @param down if this is 1, it implies a press event, 0 implies a release
+ * event. (default: 1)
+ * @param queue a queued key press means that the button event is
+ * executed just once after which the next key press is processed.
+ * It can be used for macros. Currently there is no support for
+ * time delays between queued presses. (default: 0)
+ * @param amount unimplemented for now; in the future it will be used for
+ * specifying magnitude of analog key press events
+ * @param axis
+ */
+ public void sendButton(short code, boolean repeat, boolean down, boolean queue, short amount, byte axis) throws IOException
+ {
+ PacketBUTTON p = new PacketBUTTON(code, repeat, down, queue, amount, axis);
+ p.send(hostAddress, hostPort);
+ }
+
+ /**
+ * Sends a Button event
+ * @param map_name a combination of map_name and button_name refers to a
+ * mapping in the user's Keymap.xml or Lircmap.xml.
+ * map_name can be one of the following:
+ * <ul>
+ * <li>"KB" => standard keyboard map ( <keyboard> section )</li>
+ * <li>"XG" => xbox gamepad map ( <gamepad> section )</li>
+ * <li>"R1" => xbox remote map ( <remote> section )</li>
+ * <li>"R2" => xbox universal remote map ( <universalremote> section )</li>
+ * <li>"LI:devicename" => LIRC remote map where 'devicename' is the
+ * actual device's name</li></ul>
+ * @param button_name a button name defined in the map specified in map_name.
+ * For example, if map_name is "KB" refering to the <keyboard> section in Keymap.xml
+ * then, valid button_names include "printscreen", "minus", "x", etc.
+ * @param repeat this key press should repeat until released (default: 1)
+ * Note that queued pressed cannot repeat.
+ * @param down if this is 1, it implies a press event, 0 implies a release
+ * event. (default: 1)
+ * @param queue a queued key press means that the button event is
+ * executed just once after which the next key press is processed.
+ * It can be used for macros. Currently there is no support for
+ * time delays between queued presses. (default: 0)
+ * @param amount unimplemented for now; in the future it will be used for
+ * specifying magnitude of analog key press events
+ * @param axis
+ */
+ public void sendButton(String map_name, String button_name, boolean repeat, boolean down, boolean queue, short amount, byte axis) throws IOException
+ {
+ PacketBUTTON p = new PacketBUTTON(map_name, button_name, repeat, down, queue, amount, axis);
+ p.send(hostAddress, hostPort);
+ }
+
+ /**
+ * Sets the mouse position in XBMC
+ * @param x horitontal position ranging from 0 to 65535
+ * @param y vertical position ranging from 0 to 65535
+ */
+ public void sendMouse(int x, int y) throws IOException
+ {
+ PacketMOUSE p = new PacketMOUSE(x, y);
+ p.send(hostAddress, hostPort);
+ }
+
+ /**
+ * Sends a ping to the XBMC EventServer
+ * @throws IOException
+ */
+ public void ping() throws IOException
+ {
+ PacketPING p = new PacketPING();
+ p.send(hostAddress, hostPort);
+ }
+
+ /**
+ * Tells XBMC to log the message to xbmc.log with the loglevel as specified.
+ * @param loglevel the loglevel, follows XBMC standard.
+ * <ul>
+ * <li>0 = DEBUG</li>
+ * <li>1 = INFO</li>
+ * <li>2 = NOTICE</li>
+ * <li>3 = WARNING</li>
+ * <li>4 = ERROR</li>
+ * <li>5 = SEVERE</li>
+ * </ul>
+ * @param logmessage the message to log
+ */
+ public void sendLog(byte loglevel, String logmessage) throws IOException
+ {
+ PacketLOG p = new PacketLOG(loglevel, logmessage);
+ p.send(hostAddress, hostPort);
+ }
+
+ /**
+ * Tells XBMC to do the action specified, based on the type it knows were it needs to be sent.
+ * @param actionmessage Actionmessage (as in scripting/skinning)
+ */
+ public void sendAction(String actionmessage) throws IOException
+ {
+ PacketACTION p = new PacketACTION(actionmessage);
+ p.send(hostAddress, hostPort);
+ }
+
+ /**
+ * Implements a PingThread which tells XBMC EventServer that the Client is alive (this should
+ * be done at least every 60 seconds!
+ * @author Stefan Agner
+ *
+ */
+ class PingThread extends Thread
+ {
+ private InetAddress hostAddress;
+ private int hostPort;
+ private int sleepTime;
+ private boolean giveup = false;
+
+ public PingThread(InetAddress hostAddress, int hostPort, int sleepTime)
+ {
+ super("XBMC EventClient Ping-Thread");
+ this.hostAddress = hostAddress;
+ this.hostPort = hostPort;
+ this.sleepTime = sleepTime;
+ }
+
+ public void giveup()
+ {
+ giveup = true;
+ }
+
+ public void run()
+ {
+ while(!giveup)
+ {
+ try {
+ PacketPING p = new PacketPING();
+ p.send(hostAddress, hostPort);
+ } catch (IOException e) {
+
+ e.printStackTrace();
+ }
+
+ try {
+ Thread.sleep(sleepTime);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+}
diff --git a/tools/EventClients/lib/python/__init__.py b/tools/EventClients/lib/python/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tools/EventClients/lib/python/__init__.py
diff --git a/tools/EventClients/lib/python/bt/__init__.py b/tools/EventClients/lib/python/bt/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tools/EventClients/lib/python/bt/__init__.py
diff --git a/tools/EventClients/lib/python/bt/bt.py b/tools/EventClients/lib/python/bt/bt.py
new file mode 100644
index 0000000000..de4fd1bb65
--- /dev/null
+++ b/tools/EventClients/lib/python/bt/bt.py
@@ -0,0 +1,65 @@
+BLUEZ=0
+
+try:
+ import bluetooth
+ BLUEZ=1
+except:
+ try:
+ import lightblue
+ except:
+ print "ERROR: You need to have either LightBlue or PyBluez installed\n"\
+ " in order to use this program."
+ print "- PyBluez (Linux / Windows XP) http://org.csail.mit.edu/pybluez/"
+ print "- LightBlue (Mac OS X / Linux) http://lightblue.sourceforge.net/"
+ exit()
+
+def bt_create_socket():
+ if BLUEZ:
+ sock = bluetooth.BluetoothSocket(bluetooth.L2CAP)
+ else:
+ sock = lightblue.socket(lightblue.L2CAP)
+ return sock
+
+def bt_create_rfcomm_socket():
+ if BLUEZ:
+ sock = bluetooth.BluetoothSocket( bluetooth.RFCOMM )
+ sock.bind(("",bluetooth.PORT_ANY))
+ else:
+ sock = lightblue.socket(lightblue.RFCOMM)
+ sock.bind(("",0))
+ return sock
+
+def bt_discover_devices():
+ if BLUEZ:
+ nearby = bluetooth.discover_devices()
+ else:
+ nearby = lightblue.finddevices()
+ return nearby
+
+def bt_lookup_name(bdaddr):
+ if BLUEZ:
+ bname = bluetooth.lookup_name( bdaddr )
+ else:
+ bname = bdaddr[1]
+ return bname
+
+def bt_lookup_addr(bdaddr):
+ if BLUEZ:
+ return bdaddr
+ else:
+ return bdaddr[0]
+
+def bt_advertise(name, uuid, socket):
+ if BLUEZ:
+ bluetooth.advertise_service( socket, name,
+ service_id = uuid,
+ service_classes = [ uuid, bluetooth.SERIAL_PORT_CLASS ],
+ profiles = [ bluetooth.SERIAL_PORT_PROFILE ] )
+ else:
+ lightblue.advertise(name, socket, lightblue.RFCOMM)
+
+def bt_stop_advertising(socket):
+ if BLUEZ:
+ stop_advertising(socket)
+ else:
+ lightblue.stopadvertise(socket)
diff --git a/tools/EventClients/lib/python/bt/hid.py b/tools/EventClients/lib/python/bt/hid.py
new file mode 100644
index 0000000000..7017185dc4
--- /dev/null
+++ b/tools/EventClients/lib/python/bt/hid.py
@@ -0,0 +1,54 @@
+from bluetooth import *
+
+class HID:
+ def __init__(self, bdaddress=None):
+ self.cport = 0x11 # HID's control PSM
+ self.iport = 0x13 # HID' interrupt PSM
+ self.backlog = 1
+
+ self.address = ""
+ if bdaddress:
+ self.address = bdaddress
+
+ # create the HID control socket
+ self.csock = BluetoothSocket( L2CAP )
+ self.csock.bind((self.address, self.cport))
+ set_l2cap_mtu(self.csock, 64)
+ self.csock.settimeout(2)
+ self.csock.listen(self.backlog)
+
+ # create the HID interrupt socket
+ self.isock = BluetoothSocket( L2CAP )
+ self.isock.bind((self.address, self.iport))
+ set_l2cap_mtu(self.isock, 64)
+ self.isock.settimeout(2)
+ self.isock.listen(self.backlog)
+
+ self.connected = False
+
+
+ def listen(self):
+ try:
+ (self.client_csock, self.caddress) = self.csock.accept()
+ print "Accepted Control connection from %s" % self.caddress[0]
+ (self.client_isock, self.iaddress) = self.isock.accept()
+ print "Accepted Interrupt connection from %s" % self.iaddress[0]
+ self.connected = True
+ return True
+ except Exception, e:
+ self.connected = False
+ return False
+
+
+ def get_control_socket(self):
+ if self.connected:
+ return (self.client_csock, self.caddress)
+ else:
+ return None
+
+
+ def get_interrupt_socket(self):
+ if self.connected:
+ return (self.client_isock, self.iaddress)
+ else:
+ return None
diff --git a/tools/EventClients/lib/python/ps3/__init__.py b/tools/EventClients/lib/python/ps3/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tools/EventClients/lib/python/ps3/__init__.py
diff --git a/tools/EventClients/lib/python/ps3/keymaps.py b/tools/EventClients/lib/python/ps3/keymaps.py
new file mode 100644
index 0000000000..abe7dd6427
--- /dev/null
+++ b/tools/EventClients/lib/python/ps3/keymaps.py
@@ -0,0 +1,118 @@
+# PS3 Remote and Controller Keymaps
+
+keymap_remote = {
+ "16": 'power' ,#EJECT
+ "64": None ,#AUDIO
+ "65": None ,#ANGLE
+ "63": None ,#SUBTITLE
+ "0f": None ,#CLEAR
+ "28": None ,#TIME
+
+ "00": 'one' ,#1
+ "01": 'two' ,#2
+ "02": 'three' ,#3
+ "03": 'four' ,#4
+ "04": 'five' ,#5
+ "05": 'six' ,#6
+ "06": 'seven' ,#7
+ "07": 'eight' ,#8
+ "08": 'nine' ,#9
+ "09": 'zero' ,#0
+
+ "81": 'mytv' ,#RED
+ "82": 'mymusic' ,#GREEN
+ "80": 'mypictures' ,#BLUE
+ "83": 'myvideo' ,#YELLOW
+
+ "70": 'display' ,#DISPLAY
+ "1a": None ,#TOP MENU
+ "40": 'menu' ,#POP UP/MENU
+ "0e": None ,#RETURN
+
+ "5c": 'menu' ,#OPTIONS/TRIANGLE
+ "5d": 'back' ,#BACK/CIRCLE
+ "5e": 'info' ,#X
+ "5f": 'title' ,#VIEW/SQUARE
+
+ "54": 'up' ,#UP
+ "55": 'right' ,#RIGHT
+ "56": 'down' ,#DOWN
+ "57": 'left' ,#LEFT
+ "0b": 'select' ,#ENTER
+
+ "5a": 'volumeplus' ,#L1
+ "58": 'volumeminus' ,#L2
+ "51": 'Mute' ,#L3
+ "5b": 'pageplus' ,#R1
+ "59": 'pageminus' ,#R2
+ "52": None ,#R3
+
+ "43": None ,#PLAYSTATION
+ "50": None ,#SELECT
+ "53": None ,#START
+
+ "33": 'reverse' ,#<-SCAN
+ "34": 'forward' ,# SCAN->
+ "30": 'skipminus' ,#PREV
+ "31": 'skipplus' ,#NEXT
+ "60": None ,#<-SLOW/STEP
+ "61": None ,# SLOW/STEP->
+ "32": 'play' ,#PLAY
+ "38": 'stop' ,#STOP
+ "39": 'pause' ,#PAUSE
+ }
+
+
+SX_SQUARE = 32768
+SX_X = 16384
+SX_CIRCLE = 8192
+SX_TRIANGLE = 4096
+SX_R1 = 2048
+SX_R2 = 512
+SX_R3 = 4
+SX_L1 = 1024
+SX_L2 = 256
+SX_L3 = 2
+SX_DUP = 16
+SX_DDOWN = 64
+SX_DLEFT = 128
+SX_DRIGHT = 32
+SX_SELECT = 1
+SX_START = 8
+
+SX_LSTICK_X = 0
+SX_LSTICK_Y = 1
+SX_RSTICK_X = 2
+SX_RSTICK_Y = 3
+
+# (map, key, amount index, axis)
+keymap_sixaxis = {
+ SX_X : ('XG', 'A', 0, 0),
+ SX_CIRCLE : ('XG', 'B', 0, 0),
+ SX_SQUARE : ('XG', 'X', 0, 0),
+ SX_TRIANGLE : ('XG', 'Y', 0, 0),
+
+ SX_DUP : ('XG', 'dpadup', 0, 0),
+ SX_DDOWN : ('XG', 'dpaddown', 0, 0),
+ SX_DLEFT : ('XG', 'dpadleft', 0, 0),
+ SX_DRIGHT : ('XG', 'dpadright', 0, 0),
+
+ SX_START : ('XG', 'start', 0, 0),
+ SX_SELECT : ('XG', 'back', 0, 0),
+
+ SX_R1 : ('XG', 'white', 0, 0),
+ SX_R2 : ('XG', 'rightanalogtrigger', 6, 1),
+ SX_L2 : ('XG', 'leftanalogtrigger', 5, 1),
+ SX_L1 : ('XG', 'black', 0, 0),
+
+ SX_L3 : ('XG', 'leftthumbbutton', 0, 0),
+ SX_R3 : ('XG', 'rightthumbbutton', 0, 0),
+}
+
+# (data index, left map, left action, right map, right action)
+axismap_sixaxis = {
+ SX_LSTICK_X : ('XG', 'leftthumbstickleft' , 'leftthumbstickright'),
+ SX_LSTICK_Y : ('XG', 'leftthumbstickup' , 'leftthumbstickdown'),
+ SX_RSTICK_X : ('XG', 'rightthumbstickleft', 'rightthumbstickright'),
+ SX_RSTICK_Y : ('XG', 'rightthumbstickup' , 'rightthumbstickdown'),
+}
diff --git a/tools/EventClients/lib/python/ps3/sixaxis.py b/tools/EventClients/lib/python/ps3/sixaxis.py
new file mode 100644
index 0000000000..15014b8ea0
--- /dev/null
+++ b/tools/EventClients/lib/python/ps3/sixaxis.py
@@ -0,0 +1,206 @@
+#!/usr/bin/python
+
+import time
+import sys
+import struct
+import math
+import binascii
+from bluetooth import set_l2cap_mtu
+from keymaps import keymap_sixaxis
+from keymaps import axismap_sixaxis
+
+xval = 0
+yval = 0
+num_samples = 16
+sumx = [0] * num_samples
+sumy = [0] * num_samples
+sumr = [0] * num_samples
+axis_amount = [0, 0, 0, 0]
+
+def normalize(val):
+ upperlimit = 65281
+ lowerlimit = 2
+ val_range = upperlimit - lowerlimit
+ offset = 10000
+
+ val = (val + val_range / 2) % val_range
+ upperlimit -= offset
+ lowerlimit += offset
+
+ if val < lowerlimit:
+ val = lowerlimit
+ if val > upperlimit:
+ val = upperlimit
+
+ val = ((float(val) - offset) / (float(upperlimit) -
+ lowerlimit)) * 65535.0
+ if val <= 0:
+ val = 1
+ return val
+
+def normalize_axis(val, deadzone):
+
+ val = float(val) - 127.5
+ val = val / 127.5
+
+ if abs(val) < deadzone:
+ return 0.0
+
+ if val > 0.0:
+ val = (val - deadzone) / (1.0 - deadzone)
+ else:
+ val = (val + deadzone) / (1.0 - deadzone)
+
+ return 65536.0 * val
+
+def normalize_angle(val, valrange):
+ valrange *= 2
+
+ val = val / valrange
+ if val > 1.0:
+ val = 1.0
+ if val < -1.0:
+ val = -1.0
+ return (val + 0.5) * 65535.0
+
+def initialize(control_sock, interrupt_sock):
+ # sixaxis needs this to enable it
+ # 0x53 => HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE
+ control_sock.send("\x53\xf4\x42\x03\x00\x00")
+ time.sleep(0.25)
+ data = control_sock.recv(1)
+
+ set_l2cap_mtu(control_sock, 64)
+ set_l2cap_mtu(interrupt_sock, 64)
+
+ # This command will turn on the gyro and set the leds
+ # I wonder if turning on the gyro makes it draw more current??
+ # it's probably a flag somewhere in the following command
+
+ # HID Command: HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_OUTPUT
+ # HID Report:1
+ bytes = [0x52, 0x1]
+ bytes.extend([0x00, 0x00, 0x00])
+ bytes.extend([0xFF, 0x72])
+ bytes.extend([0x00, 0x00, 0x00, 0x00])
+ bytes.extend([0x02]) # 0x02 LED1, 0x04 LED2 ... 0x10 LED4
+ # The following sections should set the blink frequncy of
+ # the leds on the controller, but i've not figured out how.
+ # These values where suggusted in a mailing list, but no explination
+ # for how they should be combined to the 5 bytes per led
+ #0xFF = 0.5Hz
+ #0x80 = 1Hz
+ #0x40 = 2Hz
+ bytes.extend([0xFF, 0x00, 0x01, 0x00, 0x01]) #LED4 [0xff, 0xff, 0x10, 0x10, 0x10]
+ bytes.extend([0xFF, 0x00, 0x01, 0x00, 0x01]) #LED3 [0xff, 0x40, 0x08, 0x10, 0x10]
+ bytes.extend([0xFF, 0x00, 0x01, 0x00, 0x01]) #LED2 [0xff, 0x00, 0x10, 0x30, 0x30]
+ bytes.extend([0xFF, 0x00, 0x01, 0x00, 0x01]) #LED1 [0xff, 0x00, 0x10, 0x40, 0x10]
+ bytes.extend([0x00, 0x00, 0x00, 0x00, 0x00])
+ bytes.extend([0x00, 0x00, 0x00, 0x00, 0x00])
+
+ control_sock.send(struct.pack("42B", *bytes))
+ time.sleep(0.25)
+ data = control_sock.recv(1)
+
+
+ return data
+
+
+def read_input(isock):
+ return isock.recv(50)
+
+
+def process_input(data, xbmc=None, mouse_enabled=0):
+ if len(data) < 3:
+ return (0, 0, 0)
+
+ # make sure this is the correct report
+ if struct.unpack("BBB", data[0:3]) != (0xa1, 0x01, 0x00):
+ return (0, 0, 0)
+
+ if len(data) >= 48:
+ v1 = struct.unpack("h", data[42:44])
+ v2 = struct.unpack("h", data[44:46])
+ v3 = struct.unpack("h", data[46:48])
+ else:
+ v1 = [0,0]
+ v2 = [0,0]
+ v3 = [0,0]
+
+ if len(data) >= 50:
+ v4 = struct.unpack("h", data[48:50])
+ else:
+ v4 = [0,0]
+
+ ax = float(v1[0])
+ ay = float(v2[0])
+ az = float(v3[0])
+ rz = float(v4[0])
+ at = math.sqrt(ax*ax + ay*ay + az*az)
+
+ bflags = struct.unpack("H", data[3:5])[0]
+ psflags = struct.unpack("B", data[5:6])[0]
+ if len(data) > 27:
+ pressure = struct.unpack("BBBBBBBBBBBB", data[15:27])
+ else:
+ pressure = [0,0,0,0,0,0,0,0,0,0,0,0,0]
+
+ roll = -math.atan2(ax, math.sqrt(ay*ay + az*az))
+ pitch = math.atan2(ay, math.sqrt(ax*ax + az*az))
+
+ pitch -= math.radians(20);
+
+ xpos = normalize_angle(roll, math.radians(30))
+ ypos = normalize_angle(pitch, math.radians(30))
+
+ # update our sliding window array
+ sumx.insert(0, xpos)
+ sumy.insert(0, ypos)
+ sumx.pop(num_samples)
+ sumy.pop(num_samples)
+
+ # reset average
+ xval = 0
+ yval = 0
+
+ # do a sliding window average to remove high frequency
+ # noise in accelerometer sampling
+ for i in range(0, num_samples):
+ xval += sumx[i]
+ yval += sumy[i]
+
+ axis = struct.unpack("BBBB", data[7:11])
+ if xbmc:
+ for i in range(4):
+ config = axismap_sixaxis[i]
+ axis_amount[i] = send_singleaxis(xbmc, axis[i], axis_amount[i], config[0], config[1], config[2])
+
+ # send the mouse position to xbmc
+ if mouse_enabled == 1:
+ xbmc.send_mouse_position(xval/num_samples, yval/num_samples)
+
+ return (bflags, psflags, pressure)
+
+def send_singleaxis(xbmc, axis, last_amount, mapname, action_min, action_pos):
+ amount = normalize_axis(axis, 0.30)
+ if last_amount < 0:
+ last_action = action_min
+ elif last_amount > 0:
+ last_action = action_pos
+ else:
+ last_action = None
+
+ if amount < 0:
+ new_action = action_min
+ elif amount > 0:
+ new_action = action_pos
+ else:
+ new_action = None
+
+ if last_action and new_action != last_action:
+ xbmc.send_button_state(map=mapname, button=last_action, amount=0, axis=1)
+
+ if new_action and amount != last_amount:
+ xbmc.send_button_state(map=mapname, button=new_action, amount=abs(amount), axis=1)
+
+ return amount
diff --git a/tools/EventClients/lib/python/xbmcclient.py b/tools/EventClients/lib/python/xbmcclient.py
new file mode 100644
index 0000000000..d62f2b9ee3
--- /dev/null
+++ b/tools/EventClients/lib/python/xbmcclient.py
@@ -0,0 +1,621 @@
+#!/usr/bin/python
+
+"""
+Implementation of XBMC's UDP based input system.
+
+A set of classes that abstract the various packets that the event server
+currently supports. In addition, there's also a class, XBMCClient, that
+provides functions that sends the various packets. Use XBMCClient if you
+don't need complete control over packet structure.
+
+The basic workflow involves:
+
+1. Send a HELO packet
+2. Send x number of valid packets
+3. Send a BYE packet
+
+IMPORTANT NOTE ABOUT TIMEOUTS:
+A client is considered to be timed out if XBMC doesn't received a packet
+at least once every 60 seconds. To "ping" XBMC with an empty packet use
+PacketPING or XBMCClient.ping(). See the documentation for details.
+"""
+
+__author__ = "d4rk@xbmc.org"
+__version__ = "0.0.3"
+
+from struct import pack
+from socket import *
+import time
+
+MAX_PACKET_SIZE = 1024
+HEADER_SIZE = 32
+MAX_PAYLOAD_SIZE = MAX_PACKET_SIZE - HEADER_SIZE
+UNIQUE_IDENTIFICATION = (int)(time.time())
+
+PT_HELO = 0x01
+PT_BYE = 0x02
+PT_BUTTON = 0x03
+PT_MOUSE = 0x04
+PT_PING = 0x05
+PT_BROADCAST = 0x06
+PT_NOTIFICATION = 0x07
+PT_BLOB = 0x08
+PT_LOG = 0x09
+PT_ACTION = 0x0A
+PT_DEBUG = 0xFF
+
+ICON_NONE = 0x00
+ICON_JPEG = 0x01
+ICON_PNG = 0x02
+ICON_GIF = 0x03
+
+BT_USE_NAME = 0x01
+BT_DOWN = 0x02
+BT_UP = 0x04
+BT_USE_AMOUNT = 0x08
+BT_QUEUE = 0x10
+BT_NO_REPEAT = 0x20
+BT_VKEY = 0x40
+BT_AXIS = 0x80
+BT_AXISSINGLE = 0x100
+
+MS_ABSOLUTE = 0x01
+
+LOGDEBUG = 0x00
+LOGINFO = 0x01
+LOGNOTICE = 0x02
+LOGWARNING = 0x03
+LOGERROR = 0x04
+LOGSEVERE = 0x05
+LOGFATAL = 0x06
+LOGNONE = 0x07
+
+ACTION_EXECBUILTIN = 0x01
+ACTION_BUTTON = 0x02
+
+######################################################################
+# Helper Functions
+######################################################################
+
+def format_string(msg):
+ """ """
+ return msg + "\0"
+
+def format_uint32(num):
+ """ """
+ return pack ("!I", num)
+
+def format_uint16(num):
+ """ """
+ if num<0:
+ num = 0
+ elif num>65535:
+ num = 65535
+ return pack ("!H", num)
+
+
+######################################################################
+# Packet Classes
+######################################################################
+
+class Packet:
+ """Base class that implements a single event packet.
+
+ - Generic packet structure (maximum 1024 bytes per packet)
+ - Header is 32 bytes long, so 992 bytes available for payload
+ - large payloads can be split into multiple packets using H4 and H5
+ H5 should contain total no. of packets in such a case
+ - H6 contains length of P1, which is limited to 992 bytes
+ - if H5 is 0 or 1, then H4 will be ignored (single packet msg)
+ - H7 must be set to zeros for now
+
+ -----------------------------
+ | -H1 Signature ("XBMC") | - 4 x CHAR 4B
+ | -H2 Version (eg. 2.0) | - 2 x UNSIGNED CHAR 2B
+ | -H3 PacketType | - 1 x UNSIGNED SHORT 2B
+ | -H4 Sequence number | - 1 x UNSIGNED LONG 4B
+ | -H5 No. of packets in msg | - 1 x UNSIGNED LONG 4B
+ | -H7 Client's unique token | - 1 x UNSIGNED LONG 4B
+ | -H8 Reserved | - 10 x UNSIGNED CHAR 10B
+ |---------------------------|
+ | -P1 payload | -
+ -----------------------------
+ """
+ def __init__(self):
+ self.sig = "XBMC"
+ self.minver = 0
+ self.majver = 2
+ self.seq = 1
+ self.maxseq = 1
+ self.payloadsize = 0
+ self.uid = UNIQUE_IDENTIFICATION
+ self.reserved = "\0" * 10
+ self.payload = ""
+ return
+
+
+ def append_payload(self, blob):
+ """Append to existing payload
+
+ Arguments:
+ blob -- binary data to append to the current payload
+ """
+ self.set_payload(self.payload + blob)
+
+
+ def set_payload(self, payload):
+ """Set the payload for this packet
+
+ Arguments:
+ payload -- binary data that contains the payload
+ """
+ self.payload = payload
+ self.payloadsize = len(self.payload)
+ self.maxseq = int((self.payloadsize + (MAX_PAYLOAD_SIZE - 1)) / MAX_PAYLOAD_SIZE)
+
+
+ def num_packets(self):
+ """ Return the number of packets required for payload """
+ return self.maxseq
+
+ def get_header(self, packettype=-1, seq=1, maxseq=1, payload_size=0):
+ """Construct a header and return as string
+
+ Keyword arguments:
+ packettype -- valid packet types are PT_HELO, PT_BYE, PT_BUTTON,
+ PT_MOUSE, PT_PING, PT_BORADCAST, PT_NOTIFICATION,
+ PT_BLOB, PT_DEBUG
+ seq -- the sequence of this packet for a multi packet message
+ (default 1)
+ maxseq -- the total number of packets for a multi packet message
+ (default 1)
+ payload_size -- the size of the payload of this packet (default 0)
+ """
+ if packettype < 0:
+ packettype = self.packettype
+ header = self.sig
+ header += chr(self.majver)
+ header += chr(self.minver)
+ header += format_uint16(packettype)
+ header += format_uint32(seq)
+ header += format_uint32(maxseq)
+ header += format_uint16(payload_size)
+ header += format_uint32(self.uid)
+ header += self.reserved
+ return header
+
+ def get_payload_size(self, seq):
+ """Returns the calculated payload size for the particular packet
+
+ Arguments:
+ seq -- the sequence number
+ """
+ if self.maxseq == 1:
+ return self.payloadsize
+
+ if seq < self.maxseq:
+ return MAX_PAYLOAD_SIZE
+
+ return self.payloadsize % MAX_PAYLOAD_SIZE
+
+
+ def get_udp_message(self, packetnum=1):
+ """Construct the UDP message for the specified packetnum and return
+ as string
+
+ Keyword arguments:
+ packetnum -- the packet no. for which to construct the message
+ (default 1)
+ """
+ if packetnum > self.num_packets() or packetnum < 1:
+ return ""
+ header = ""
+ if packetnum==1:
+ header = self.get_header(self.packettype, packetnum, self.maxseq,
+ self.get_payload_size(packetnum))
+ else:
+ header = self.get_header(PT_BLOB, packetnum, self.maxseq,
+ self.get_payload_size(packetnum))
+
+ payload = self.payload[ (packetnum-1) * MAX_PAYLOAD_SIZE :
+ (packetnum-1) * MAX_PAYLOAD_SIZE+
+ self.get_payload_size(packetnum) ]
+ return header + payload
+
+ def send(self, sock, addr, uid=UNIQUE_IDENTIFICATION):
+ """Send the entire message to the specified socket and address.
+
+ Arguments:
+ sock -- datagram socket object (socket.socket)
+ addr -- address, port pair (eg: ("127.0.0.1", 9777) )
+ uid -- unique identification
+ """
+ self.uid = uid
+ for a in range ( 0, self.num_packets() ):
+ try:
+ sock.sendto(self.get_udp_message(a+1), addr)
+ return True
+ except:
+ return False
+
+
+class PacketHELO (Packet):
+ """A HELO packet
+
+ A HELO packet establishes a valid connection to XBMC. It is the
+ first packet that should be sent.
+ """
+ def __init__(self, devicename=None, icon_type=ICON_NONE, icon_file=None):
+ """
+ Keyword arguments:
+ devicename -- the string that identifies the client
+ icon_type -- one of ICON_NONE, ICON_JPEG, ICON_PNG, ICON_GIF
+ icon_file -- location of icon file with respect to current working
+ directory if icon_type is not ICON_NONE
+ """
+ Packet.__init__(self)
+ self.packettype = PT_HELO
+ self.icontype = icon_type
+ self.set_payload ( format_string(devicename)[0:128] )
+ self.append_payload( chr (icon_type) )
+ self.append_payload( format_uint16 (0) ) # port no
+ self.append_payload( format_uint32 (0) ) # reserved1
+ self.append_payload( format_uint32 (0) ) # reserved2
+ if icon_type != ICON_NONE and icon_file:
+ self.append_payload( file(icon_file).read() )
+
+class PacketNOTIFICATION (Packet):
+ """A NOTIFICATION packet
+
+ This packet displays a notification window in XBMC. It can contain
+ a caption, a message and an icon.
+ """
+ def __init__(self, title, message, icon_type=ICON_NONE, icon_file=None):
+ """
+ Keyword arguments:
+ title -- the notification caption / title
+ message -- the main text of the notification
+ icon_type -- one of ICON_NONE, ICON_JPEG, ICON_PNG, ICON_GIF
+ icon_file -- location of icon file with respect to current working
+ directory if icon_type is not ICON_NONE
+ """
+ Packet.__init__(self)
+ self.packettype = PT_NOTIFICATION
+ self.title = title
+ self.message = message
+ self.set_payload ( format_string(title) )
+ self.append_payload( format_string(message) )
+ self.append_payload( chr (icon_type) )
+ self.append_payload( format_uint32 (0) ) # reserved
+ if icon_type != ICON_NONE and icon_file:
+ self.append_payload( file(icon_file).read() )
+
+class PacketBUTTON (Packet):
+ """A BUTTON packet
+
+ A button packet send a key press or release event to XBMC
+ """
+ def __init__(self, code=0, repeat=1, down=1, queue=0,
+ map_name="", button_name="", amount=0, axis=0):
+ """
+ Keyword arguments:
+ code -- raw button code (default: 0)
+ repeat -- this key press should repeat until released (default: 1)
+ Note that queued pressed cannot repeat.
+ down -- if this is 1, it implies a press event, 0 implies a release
+ event. (default: 1)
+ queue -- a queued key press means that the button event is
+ executed just once after which the next key press is
+ processed. It can be used for macros. Currently there
+ is no support for time delays between queued presses.
+ (default: 0)
+ map_name -- a combination of map_name and button_name refers to a
+ mapping in the user's Keymap.xml or Lircmap.xml.
+ map_name can be one of the following:
+ "KB" => standard keyboard map ( <keyboard> section )
+ "XG" => xbox gamepad map ( <gamepad> section )
+ "R1" => xbox remote map ( <remote> section )
+ "R2" => xbox universal remote map ( <universalremote>
+ section )
+ "LI:devicename" => LIRC remote map where 'devicename' is the
+ actual device's name
+ button_name -- a button name defined in the map specified in map_name.
+ For example, if map_name is "KB" refering to the
+ <keyboard> section in Keymap.xml then, valid
+ button_names include "printscreen", "minus", "x", etc.
+ amount -- unimplemented for now; in the future it will be used for
+ specifying magnitude of analog key press events
+ """
+ Packet.__init__(self)
+ self.flags = 0
+ self.packettype = PT_BUTTON
+ if type (code ) == str:
+ code = ord(code)
+
+ # assign code only if we don't have a map and button name
+ if not (map_name and button_name):
+ self.code = code
+ else:
+ self.flags |= BT_USE_NAME
+ self.code = 0
+ if (amount != None):
+ self.flags |= BT_USE_AMOUNT
+ self.amount = int(amount)
+ else:
+ self.amount = 0
+
+ if down:
+ self.flags |= BT_DOWN
+ else:
+ self.flags |= BT_UP
+ if not repeat:
+ self.flags |= BT_NO_REPEAT
+ if queue:
+ self.flags |= BT_QUEUE
+ if axis == 1:
+ self.flags |= BT_AXISSINGLE
+ elif axis == 2:
+ self.flags |= BT_AXIS
+
+ self.set_payload ( format_uint16(self.code) )
+ self.append_payload( format_uint16(self.flags) )
+ self.append_payload( format_uint16(self.amount) )
+ self.append_payload( format_string (map_name) )
+ self.append_payload( format_string (button_name) )
+
+class PacketMOUSE (Packet):
+ """A MOUSE packet
+
+ A MOUSE packets sets the mouse position in XBMC
+ """
+ def __init__(self, x, y):
+ """
+ Arguments:
+ x -- horitontal position ranging from 0 to 65535
+ y -- vertical position ranging from 0 to 65535
+
+ The range will be mapped to the screen width and height in XBMC
+ """
+ Packet.__init__(self)
+ self.packettype = PT_MOUSE
+ self.flags = MS_ABSOLUTE
+ self.append_payload( chr (self.flags) )
+ self.append_payload( format_uint16(x) )
+ self.append_payload( format_uint16(y) )
+
+class PacketBYE (Packet):
+ """A BYE packet
+
+ A BYE packet terminates the connection to XBMC.
+ """
+ def __init__(self):
+ Packet.__init__(self)
+ self.packettype = PT_BYE
+
+
+class PacketPING (Packet):
+ """A PING packet
+
+ A PING packet tells XBMC that the client is still alive. All valid
+ packets act as ping (not just this one). A client needs to ping
+ XBMC at least once in 60 seconds or it will time out.
+ """
+ def __init__(self):
+ Packet.__init__(self)
+ self.packettype = PT_PING
+
+class PacketLOG (Packet):
+ """A LOG packet
+
+ A LOG packet tells XBMC to log the message to xbmc.log with the loglevel as specified.
+ """
+ def __init__(self, loglevel=0, logmessage="", autoprint=True):
+ """
+ Keyword arguments:
+ loglevel -- the loglevel, follows XBMC standard.
+ logmessage -- the message to log
+ autoprint -- if the logmessage should automaticly be printed to stdout
+ """
+ Packet.__init__(self)
+ self.packettype = PT_LOG
+ self.append_payload( chr (loglevel) )
+ self.append_payload( format_string(logmessage) )
+ if (autoprint):
+ print logmessage
+
+class PacketACTION (Packet):
+ """An ACTION packet
+
+ An ACTION packet tells XBMC to do the action specified, based on the type it knows were it needs to be sent.
+ The idea is that this will be as in scripting/skining and keymapping, just triggered from afar.
+ """
+ def __init__(self, actionmessage="", actiontype=ACTION_EXECBUILTIN):
+ """
+ Keyword arguments:
+ loglevel -- the loglevel, follows XBMC standard.
+ logmessage -- the message to log
+ autoprint -- if the logmessage should automaticly be printed to stdout
+ """
+ Packet.__init__(self)
+ self.packettype = PT_ACTION
+ self.append_payload( chr (actiontype) )
+ self.append_payload( format_string(actionmessage) )
+
+######################################################################
+# XBMC Client Class
+######################################################################
+
+class XBMCClient:
+ """An XBMC event client"""
+
+ def __init__(self, name ="", icon_file=None, broadcast=False, uid=UNIQUE_IDENTIFICATION,
+ ip="127.0.0.1"):
+ """
+ Keyword arguments:
+ name -- Name of the client
+ icon_file -- location of an icon file, if any (png, jpg or gif)
+ uid -- unique identification
+ """
+ self.name = str(name)
+ self.icon_file = icon_file
+ self.icon_type = self._get_icon_type(icon_file)
+ self.ip = ip
+ self.port = 9777
+ self.sock = socket(AF_INET,SOCK_DGRAM)
+ if broadcast:
+ self.sock.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
+ self.uid = uid
+
+
+ def connect(self, ip=None, port=None):
+ """Initialize connection to XBMC
+ ip -- IP Address of XBMC
+ port -- port that the event server on XBMC is listening on
+ """
+ if ip:
+ self.ip = ip
+ if port:
+ self.port = int(port)
+ self.addr = (self.ip, self.port)
+ packet = PacketHELO(self.name, self.icon_type, self.icon_file)
+ return packet.send(self.sock, self.addr, self.uid)
+
+
+ def close(self):
+ """Close the current connection"""
+ packet = PacketBYE()
+ return packet.send(self.sock, self.addr, self.uid)
+
+
+ def ping(self):
+ """Send a PING packet"""
+ packet = PacketPING()
+ return packet.send(self.sock, self.addr, self.uid)
+
+
+ def send_notification(self, title="", message="", icon_file=None):
+ """Send a notification to the connected XBMC
+ Keyword Arguments:
+ title -- The title/heading for the notifcation
+ message -- The message to be displayed
+ icon_file -- location of an icon file, if any (png, jpg, gif)
+ """
+ self.connect()
+ packet = PacketNOTIFICATION(title, message,
+ self._get_icon_type(icon_file),
+ icon_file)
+ return packet.send(self.sock, self.addr, self.uid)
+
+
+ def send_keyboard_button(self, button=None):
+ """Send a keyboard event to XBMC
+ Keyword Arguments:
+ button -- name of the keyboard button to send (same as in Keymap.xml)
+ """
+ if not button:
+ return
+ return self.send_button(map="KB", button=button)
+
+
+ def send_remote_button(self, button=None):
+ """Send a remote control event to XBMC
+ Keyword Arguments:
+ button -- name of the remote control button to send (same as in Keymap.xml)
+ """
+ if not button:
+ return
+ return self.send_button(map="R1", button=button)
+
+
+ def release_button(self):
+ """Release all buttons"""
+ packet = PacketBUTTON(code=0x01, down=0)
+ return packet.send(self.sock, self.addr, self.uid)
+
+
+ def send_button(self, map="", button="", amount=0):
+ """Send a button event to XBMC
+ Keyword arguments:
+ map -- a combination of map_name and button_name refers to a
+ mapping in the user's Keymap.xml or Lircmap.xml.
+ map_name can be one of the following:
+ "KB" => standard keyboard map ( <keyboard> section )
+ "XG" => xbox gamepad map ( <gamepad> section )
+ "R1" => xbox remote map ( <remote> section )
+ "R2" => xbox universal remote map ( <universalremote>
+ section )
+ "LI:devicename" => LIRC remote map where 'devicename' is the
+ actual device's name
+ button -- a button name defined in the map specified in map, above.
+ For example, if map is "KB" refering to the <keyboard>
+ section in Keymap.xml then, valid buttons include
+ "printscreen", "minus", "x", etc.
+ """
+ packet = PacketBUTTON(map_name=str(map), button_name=str(button), amount=amount)
+ return packet.send(self.sock, self.addr, self.uid)
+
+ def send_button_state(self, map="", button="", amount=0, down=0, axis=0):
+ """Send a button event to XBMC
+ Keyword arguments:
+ map -- a combination of map_name and button_name refers to a
+ mapping in the user's Keymap.xml or Lircmap.xml.
+ map_name can be one of the following:
+ "KB" => standard keyboard map ( <keyboard> section )
+ "XG" => xbox gamepad map ( <gamepad> section )
+ "R1" => xbox remote map ( <remote> section )
+ "R2" => xbox universal remote map ( <universalremote>
+ section )
+ "LI:devicename" => LIRC remote map where 'devicename' is the
+ actual device's name
+ button -- a button name defined in the map specified in map, above.
+ For example, if map is "KB" refering to the <keyboard>
+ section in Keymap.xml then, valid buttons include
+ "printscreen", "minus", "x", etc.
+ """
+ if axis:
+ if amount == 0:
+ down = 0
+ else:
+ down = 1
+
+ packet = PacketBUTTON(map_name=str(map), button_name=str(button), amount=amount, down=down, queue=1, axis=axis)
+ return packet.send(self.sock, self.addr, self.uid)
+
+ def send_mouse_position(self, x=0, y=0):
+ """Send a mouse event to XBMC
+ Keywords Arguments:
+ x -- absolute x position of mouse ranging from 0 to 65535
+ which maps to the entire screen width
+ y -- same a 'x' but relates to the screen height
+ """
+ packet = PacketMOUSE(int(x), int(y))
+ return packet.send(self.sock, self.addr, self.uid)
+
+ def send_log(self, loglevel=0, logmessage="", autoprint=True):
+ """
+ Keyword arguments:
+ loglevel -- the loglevel, follows XBMC standard.
+ logmessage -- the message to log
+ autoprint -- if the logmessage should automaticly be printed to stdout
+ """
+ packet = PacketLOG(loglevel, logmessage)
+ return packet.send(self.sock, self.addr, self.uid)
+
+ def send_action(self, actionmessage="", actiontype=ACTION_EXECBUILTIN):
+ """
+ Keyword arguments:
+ actionmessage -- the ActionString
+ actiontype -- The ActionType the ActionString should be sent to.
+ """
+ packet = PacketACTION(actionmessage, actiontype)
+ return packet.send(self.sock, self.addr, self.uid)
+
+ def _get_icon_type(self, icon_file):
+ if icon_file:
+ if icon_file.lower()[-3:] == "png":
+ return ICON_PNG
+ elif icon_file.lower()[-3:] == "gif":
+ return ICON_GIF
+ elif icon_file.lower()[-3:] == "jpg":
+ return ICON_JPG
+ return ICON_NONE
diff --git a/tools/EventClients/lib/python/zeroconf.py b/tools/EventClients/lib/python/zeroconf.py
new file mode 100644
index 0000000000..9fc82f5909
--- /dev/null
+++ b/tools/EventClients/lib/python/zeroconf.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+"""
+Simple wrapper around Avahi
+"""
+
+__author__ = "d4rk@xbmc.org"
+__version__ = "0.1"
+
+try:
+ import time
+ import dbus, gobject, avahi
+ from dbus import DBusException
+ from dbus.mainloop.glib import DBusGMainLoop
+except Exception, e:
+ print "Zeroconf support disabled. To enable, install the following Python modules:"
+ print " dbus, gobject, avahi"
+ pass
+
+SERVICE_FOUND = 1
+SERVICE_LOST = 2
+
+class Browser:
+ """ Simple Zeroconf Browser """
+
+ def __init__( self, service_types = {} ):
+ """
+ service_types - dictionary of services => handlers
+ """
+ self._stop = False
+ self.loop = DBusGMainLoop()
+ self.bus = dbus.SystemBus( mainloop=self.loop )
+ self.server = dbus.Interface( self.bus.get_object( avahi.DBUS_NAME, '/' ),
+ 'org.freedesktop.Avahi.Server')
+ self.handlers = {}
+
+ for type in service_types.keys():
+ self.add_service( type, service_types[ type ] )
+
+
+ def add_service( self, type, handler = None ):
+ """
+ Add a service that the browser should watch for
+ """
+ self.sbrowser = dbus.Interface(
+ self.bus.get_object(
+ avahi.DBUS_NAME,
+ self.server.ServiceBrowserNew(
+ avahi.IF_UNSPEC,
+ avahi.PROTO_UNSPEC,
+ type,
+ 'local',
+ dbus.UInt32(0)
+ )
+ ),
+ avahi.DBUS_INTERFACE_SERVICE_BROWSER)
+ self.handlers[ type ] = handler
+ self.sbrowser.connect_to_signal("ItemNew", self._new_item_handler)
+ self.sbrowser.connect_to_signal("ItemRemove", self._remove_item_handler)
+
+
+ def run(self):
+ """
+ Run the gobject event loop
+ """
+ # Don't use loop.run() because Python's GIL will block all threads
+ loop = gobject.MainLoop()
+ context = loop.get_context()
+ while not self._stop:
+ if context.pending():
+ context.iteration( True )
+ else:
+ time.sleep(1)
+
+ def stop(self):
+ """
+ Stop the gobject event loop
+ """
+ self._stop = True
+
+
+ def _new_item_handler(self, interface, protocol, name, stype, domain, flags):
+ if flags & avahi.LOOKUP_RESULT_LOCAL:
+ # local service, skip
+ pass
+
+ self.server.ResolveService(
+ interface,
+ protocol,
+ name,
+ stype,
+ domain,
+ avahi.PROTO_UNSPEC,
+ dbus.UInt32(0),
+ reply_handler = self._service_resolved_handler,
+ error_handler = self._error_handler
+ )
+ return
+
+
+ def _remove_item_handler(self, interface, protocol, name, stype, domain, flags):
+ if self.handlers[ stype ]:
+ # FIXME: more details needed here
+ try:
+ self.handlers[ stype ]( SERVICE_LOST, { 'type' : stype, 'name' : name } )
+ except:
+ pass
+
+
+ def _service_resolved_handler( self, *args ):
+ service = {}
+ service['type'] = str( args[3] )
+ service['name'] = str( args[2] )
+ service['address'] = str( args[7] )
+ service['hostname'] = str( args[5] )
+ service['port'] = int( args[8] )
+
+ # if the service type has a handler call it
+ try:
+ if self.handlers[ args[3] ]:
+ self.handlers[ args[3] ]( SERVICE_FOUND, service )
+ except:
+ pass
+
+
+ def _error_handler( self, *args ):
+ print 'ERROR: %s ' % str( args[0] )
+
+
+if __name__ == "__main__":
+ def service_handler( found, service ):
+ print "---------------------"
+ print ['Found Service', 'Lost Service'][found-1]
+ for key in service.keys():
+ print key+" : "+str( service[key] )
+
+ browser = Browser( {
+ '_xbmc-events._udp' : service_handler,
+ '_xbmc-web._tcp' : service_handler
+ } )
+ browser.run()
+