aboutsummaryrefslogtreecommitdiff
path: root/tools/EventClients
diff options
context:
space:
mode:
Diffstat (limited to 'tools/EventClients')
-rw-r--r--tools/EventClients/Clients/AppleRemote/AppleRemote.cpp645
-rw-r--r--tools/EventClients/Clients/AppleRemote/AppleRemote.h80
-rw-r--r--tools/EventClients/Clients/AppleRemote/Makefile25
-rw-r--r--tools/EventClients/Clients/AppleRemote/XBox360.h576
-rw-r--r--tools/EventClients/Clients/AppleRemote/iremoted.c518
-rw-r--r--tools/EventClients/Clients/J2ME Client/README23
-rw-r--r--tools/EventClients/Clients/J2ME Client/build.xml31
-rw-r--r--tools/EventClients/Clients/J2ME Client/icons/icon.pngbin0 -> 3949 bytes
-rwxr-xr-xtools/EventClients/Clients/J2ME Client/j2me_remote.py175
-rw-r--r--tools/EventClients/Clients/J2ME Client/manifest.mf7
-rw-r--r--tools/EventClients/Clients/J2ME Client/src/BluetoothCommunicator.java228
-rw-r--r--tools/EventClients/Clients/J2ME Client/src/BluetoothEvent.java27
-rw-r--r--tools/EventClients/Clients/J2ME Client/src/BluetoothServiceDiscovery.java126
-rw-r--r--tools/EventClients/Clients/J2ME Client/src/KeyCanvas.java66
-rw-r--r--tools/EventClients/Clients/J2ME Client/src/KeyHandler.java23
-rw-r--r--tools/EventClients/Clients/J2ME Client/src/MainScreen.java271
-rw-r--r--tools/EventClients/Clients/J2ME Client/src/XBMCRemote.jad9
-rw-r--r--tools/EventClients/Clients/OSXRemote/Makefile.in18
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/AppleRemote.h44
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/AppleRemote.m108
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/GlobalKeyboardDevice.h49
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/GlobalKeyboardDevice.m241
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/HIDRemoteControlDevice.h77
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/HIDRemoteControlDevice.m547
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/IRKeyboardEmu.h16
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/IRKeyboardEmu.m26
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/KeyspanFrontRowControl.h39
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/KeyspanFrontRowControl.m87
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/MultiClickRemoteBehavior.h90
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/MultiClickRemoteBehavior.m210
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControl.h102
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControl.m102
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControlContainer.h38
-rw-r--r--tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControlContainer.m114
-rw-r--r--tools/EventClients/Clients/OSXRemote/XBMCDebugHelpers.h45
-rw-r--r--tools/EventClients/Clients/OSXRemote/XBMCHelper.h29
-rw-r--r--tools/EventClients/Clients/OSXRemote/XBMCHelper.m222
-rw-r--r--tools/EventClients/Clients/OSXRemote/XBMCHelper.xcodeproj/project.pbxproj305
-rw-r--r--tools/EventClients/Clients/OSXRemote/xbmcclientwrapper.h73
-rw-r--r--tools/EventClients/Clients/OSXRemote/xbmcclientwrapper.mm430
-rw-r--r--tools/EventClients/Clients/OSXRemote/xbmchelper_main.mm210
-rwxr-xr-xtools/EventClients/Clients/PS3 BD Remote/ps3_remote.py202
-rwxr-xr-xtools/EventClients/Clients/PS3 Sixaxis Controller/ps3d.py433
-rw-r--r--tools/EventClients/Clients/WiiRemote/CWIID_WiiRemote.cpp774
-rw-r--r--tools/EventClients/Clients/WiiRemote/CWIID_WiiRemote.h170
-rw-r--r--tools/EventClients/Clients/WiiRemote/Makefile13
-rw-r--r--tools/EventClients/Clients/WiiRemote/WiiUse_README18
-rw-r--r--tools/EventClients/Clients/WiiRemote/WiiUse_WiiRemote.cpp264
-rw-r--r--tools/EventClients/Clients/WiiRemote/WiiUse_WiiRemote.h124
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/CHANGELOG208
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/LICENSE620
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/LICENSE_noncommercial170
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/Makefile23
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/README155
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/Makefile83
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.dsp111
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.dsw29
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.optbin0 -> 48640 bytes
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/sdl.c439
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/Makefile84
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/example.c437
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.dsp106
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.dsw29
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.optbin0 -> 48640 bytes
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/Makefile91
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/classic.c190
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/classic.h53
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/definitions.h79
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/dynamics.c228
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/dynamics.h56
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/events.c878
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/events.h54
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/guitar_hero_3.c172
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/guitar_hero_3.h62
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io.c119
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io.h56
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io_nix.c270
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io_win.c247
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/ir.c748
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/ir.h56
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.dsp191
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.dsw29
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.optbin0 -> 52736 bytes
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/nunchuk.c210
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/nunchuk.h53
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/os.h56
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse.c764
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse.h653
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse_internal.h226
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/wiiuse.cbp86
-rw-r--r--tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/wiiuse.layout61
-rw-r--r--tools/EventClients/Clients/XBMC Send/xbmc-send.py61
-rw-r--r--tools/EventClients/Clients/Xbox360 Controller/Xbox360Controller.cpp154
-rw-r--r--tools/EventClients/Clients/Xbox360 Controller/Xbox360Controller.h28
-rw-r--r--tools/EventClients/Clients/Xbox360 Controller/Xbox360EventClient.cpp123
-rw-r--r--tools/EventClients/Clients/Xbox360 Controller/Xbox360EventClient.vcproj208
-rw-r--r--tools/EventClients/Clients/Xbox360 Controller/stdafx.cpp8
-rw-r--r--tools/EventClients/Clients/Xbox360 Controller/stdafx.h15
-rw-r--r--tools/EventClients/Clients/Xbox360 Controller/targetver.h13
-rw-r--r--tools/EventClients/Makefile32
-rw-r--r--tools/EventClients/README.txt101
-rw-r--r--tools/EventClients/examples/c#/XBMCDemoClient1.cs35
-rw-r--r--tools/EventClients/examples/c++/example_button1.cpp38
-rw-r--r--tools/EventClients/examples/c++/example_button2.cpp48
-rw-r--r--tools/EventClients/examples/c++/example_log.cpp34
-rw-r--r--tools/EventClients/examples/c++/example_mouse.cpp36
-rw-r--r--tools/EventClients/examples/c++/example_notification.cpp37
-rw-r--r--tools/EventClients/examples/java/XBMCDemoClient1.java52
-rwxr-xr-xtools/EventClients/examples/python/example_action.py40
-rwxr-xr-xtools/EventClients/examples/python/example_button1.py79
-rwxr-xr-xtools/EventClients/examples/python/example_button2.py73
-rwxr-xr-xtools/EventClients/examples/python/example_mouse.py45
-rwxr-xr-xtools/EventClients/examples/python/example_notification.py38
-rwxr-xr-xtools/EventClients/examples/python/example_simple.py48
-rw-r--r--tools/EventClients/icons/bluetooth.pngbin0 -> 2490 bytes
-rw-r--r--tools/EventClients/icons/mail.pngbin0 -> 1494 bytes
-rw-r--r--tools/EventClients/icons/mouse.pngbin0 -> 2616 bytes
-rw-r--r--tools/EventClients/icons/phone.pngbin0 -> 1852 bytes
-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
140 files changed, 21147 insertions, 0 deletions
diff --git a/tools/EventClients/Clients/AppleRemote/AppleRemote.cpp b/tools/EventClients/Clients/AppleRemote/AppleRemote.cpp
new file mode 100644
index 0000000000..1cd8baa730
--- /dev/null
+++ b/tools/EventClients/Clients/AppleRemote/AppleRemote.cpp
@@ -0,0 +1,645 @@
+/*
+ * AppleRemote.cpp
+ * AppleRemote
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/errno.h>
+#include <sysexits.h>
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "AppleRemote.h"
+#include "../../lib/c++/xbmcclient.h"
+
+#define DEFAULT_MAX_CLICK_DURATION 0.5
+
+#define LOG if (m_bVerbose) printf
+
+#define APPLICATION_NAME "XBMC"
+
+enum {
+ IR_Select,
+ IR_SelectHold,
+ IR_Right,
+ IR_Left,
+ IR_Up,
+ IR_Down,
+ IR_RightHold,
+ IR_LeftHold,
+ IR_Menu,
+ IR_MenuHold
+};
+enum {
+ IR_Event_Term_ATV1X = 5,
+ IR_Event_Term_ATV20X = 5,
+ IR_Event_Term_ATV21 = 8,
+ IR_Event_Term_10_4 = 5,
+ IR_Event_Term_10_5 = 18
+};
+// magic HID key cookies AppleTV running r1.x (same as 10.4)
+static std::string key_cookiesATV1X[] =
+{
+ "8_", //SelectHold = "18_"
+ "18_",
+ "9_",
+ "10_",
+ "12_",
+ "13_",
+ "4_",
+ "3_",
+ "7_",
+ "5_"
+};
+// magic HID key cookies AppleTV running r2.0x
+static std::string key_cookiesATV20X[] =
+{
+ "8_", //SelectHold = "18_"
+ "18_",
+ "9_",
+ "10_",
+ "12_",
+ "13_",
+ "4_",
+ "3_",
+ "5_",
+ "7_"
+};
+// magic HID key cookies for AppleTV running r2.1
+static std::string key_cookiesATV21[] =
+{
+ "9_", //SelectHold = "19_"
+ "19_",
+ "10_",
+ "11_",
+ "13_",
+ "14_",
+ "5_",
+ "4_",
+ "6_",
+ "8_"
+};
+// magic HID key cookies for 10.4
+static std::string key_cookies10_4[] =
+{
+ "8_", //SelectHold = "18_"
+ "18_",
+ "9_",
+ "10_",
+ "12_",
+ "13_",
+ "4_",
+ "3_",
+ "7_",
+ "5_"
+};
+
+// magic HID key cookies for 10.5
+static std::string key_cookies10_5[] =
+{
+ "21_", //SelectHold = "35_"
+ "35_",
+ "22_",
+ "23_",
+ "29_",
+ "30_",
+ "4_",
+ "3_",
+ "20_",
+ "18_"
+};
+
+AppleRemote::AppleRemote() :m_bVerbose(false),
+ m_remoteMode(REMOTE_NORMAL),
+ m_dMaxClickDuration(DEFAULT_MAX_CLICK_DURATION),
+ m_socket(-1),
+ m_timer(NULL)
+{
+ m_serverAddress = "localhost";
+ m_appPath = "";
+ m_appHome = "";
+}
+
+AppleRemote::~AppleRemote()
+{
+ DeInitialize();
+}
+
+void AppleRemote::RegisterCommand(const std::string &strSequence, CPacketBUTTON *pPacket)
+{
+ LOG("Registering command %s\n", strSequence.c_str());
+ m_mapCommands[strSequence] = pPacket;
+}
+
+void AppleRemote::Initialize()
+{
+ std::string *key;
+ std::string prefix;
+
+ LOG("initializing apple remote\n");
+
+ DeInitialize();
+
+ m_socket = socket(AF_INET, SOCK_DGRAM, 0);
+ if (m_socket < 0)
+ {
+ fprintf(stderr, "Error opening UDP socket! error: %d\n", errno);
+ exit(1);
+ }
+
+ LOG("udp socket (%d) opened\n", m_socket);
+
+ // Runtime Version Check
+ SInt32 MacVersion;
+
+ Gestalt(gestaltSystemVersion, &MacVersion);
+
+ if (MacVersion < 0x1050)
+ {
+ // OSX 10.4/AppleTV
+ size_t len = 512;
+ char buffer[512];
+ std::string hw_model = "unknown";
+
+ if (sysctlbyname("hw.model", &buffer, &len, NULL, 0) == 0)
+ hw_model = buffer;
+
+ if (hw_model == "AppleTV1,1")
+ {
+ FILE *inpipe;
+ bool atv_version_found = false;
+ char linebuf[1000];
+
+ //Find the build version of the AppleTV OS
+ inpipe = popen("sw_vers -buildVersion", "r");
+ if (inpipe)
+ {
+ //get output
+ if(fgets(linebuf, sizeof(linebuf) - 1, inpipe))
+ {
+ if( strstr(linebuf,"8N5107") || strstr(linebuf,"8N5239"))
+ {
+ // r1.0 or r1.1
+ atv_version_found = true;
+ fprintf(stderr, "Using key code for AppleTV r1.x\n");
+ key = key_cookiesATV1X;
+ m_button_event_terminator = IR_Event_Term_ATV1X;
+ }
+ else if (strstr(linebuf,"8N5400") || strstr(linebuf,"8N5455") || strstr(linebuf,"8N5461"))
+ {
+ // r2.0, r2.01 or r2.02
+ atv_version_found = true;
+ fprintf(stderr, "Using key code for AppleTV r2.0x\n");
+ key = key_cookiesATV20X;
+ m_button_event_terminator = IR_Event_Term_ATV20X;
+ }
+ else if( strstr(linebuf,"8N5519"))
+ {
+ // r2.10
+ atv_version_found = true;
+ fprintf(stderr, "Using key code for AppleTV r2.1\n");
+ key = key_cookiesATV21;
+ m_button_event_terminator = IR_Event_Term_ATV21;
+
+ }
+ else if( strstr(linebuf,"8N5622"))
+ {
+ // r2.10
+ atv_version_found = true;
+ fprintf(stderr, "Using key code for AppleTV r2.2\n");
+ key = key_cookiesATV21;
+ m_button_event_terminator = IR_Event_Term_ATV21;
+ }
+ }
+ pclose(inpipe);
+ }
+
+ if(!atv_version_found){
+ //handle fallback or just exit
+ fprintf(stderr, "AppletTV software version could not be determined.\n");
+ fprintf(stderr, "Defaulting to using key code for AppleTV r2.1\n");
+ key = key_cookiesATV21;
+ m_button_event_terminator = IR_Event_Term_ATV21;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "Using key code for OSX 10.4\n");
+ key = key_cookies10_4;
+ m_button_event_terminator = IR_Event_Term_10_4;
+ }
+ }
+ else
+ {
+ // OSX 10.5
+ fprintf(stderr, "Using key code for OSX 10.5\n");
+ key = key_cookies10_5;
+ m_button_event_terminator = IR_Event_Term_10_5;
+ }
+ m_launch_xbmc_button = key[IR_MenuHold];
+
+ RegisterCommand(key[IR_Select], new CPacketBUTTON(5, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(key[IR_SelectHold],new CPacketBUTTON(7, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(key[IR_Right], new CPacketBUTTON(4, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(key[IR_Left], new CPacketBUTTON(3, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(key[IR_Up], new CPacketBUTTON(1, "JS0:AppleRemote", BTN_DOWN));
+ RegisterCommand(key[IR_Down], new CPacketBUTTON(2, "JS0:AppleRemote", BTN_DOWN));
+ RegisterCommand(key[IR_RightHold], new CPacketBUTTON(4, "JS0:AppleRemote", BTN_DOWN));
+ RegisterCommand(key[IR_LeftHold], new CPacketBUTTON(3, "JS0:AppleRemote", BTN_DOWN));
+
+ RegisterCommand(key[IR_Menu], new CPacketBUTTON(6, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+
+ // Menu Hold will be used both for sending "Back" and for starting universal remote combinations (if universal mode is on)
+ RegisterCommand(key[IR_MenuHold], new CPacketBUTTON(8, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+
+ // Universal commmands:
+ RegisterCommand(key[IR_MenuHold] + key[IR_Down], new CPacketBUTTON("Back", "R1", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+
+ prefix = key[IR_MenuHold] + key[IR_Select];
+ RegisterCommand(prefix + key[IR_Right], new CPacketBUTTON("Info", "R1", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(prefix + key[IR_Left], new CPacketBUTTON("Title", "R1", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(prefix + key[IR_Up], new CPacketBUTTON("PagePlus", "R1", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(prefix + key[IR_Down], new CPacketBUTTON("PageMinus", "R1", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(prefix + key[IR_Select],new CPacketBUTTON("Display", "R1", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+
+ prefix = key[IR_MenuHold] + key[IR_Up];
+
+ RegisterCommand(prefix + key[IR_Select],new CPacketBUTTON("Stop", "R1", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(prefix + key[IR_Left], new CPacketBUTTON("Power", "R1", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(prefix + key[IR_Right], new CPacketBUTTON("Zero", "R1", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(prefix + key[IR_Up], new CPacketBUTTON("Play", "R1", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+ RegisterCommand(prefix + key[IR_Down], new CPacketBUTTON("Pause", "R1", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE));
+
+ // for universal mode - some keys are part of a key combination.
+ // when those keys are pressed, we'll wait for more to come - until a valid combination was entered or a timeout expired.
+ // the keys "leading" a combination are added to the prefix vector.
+ // all we need to do to turn universal mode off is clear the prefix vector and then every command will be sent immidiately.
+ if (m_remoteMode == REMOTE_UNIVERSAL)
+ {
+ LOG("universal mode on. registering prefixes: %s\n", key[IR_MenuHold].c_str());
+ m_universalPrefixes.push_back( key[IR_MenuHold] );
+ }
+ else
+ {
+ LOG("universal mode off.\n");
+ m_universalPrefixes.clear();
+ }
+
+ m_strCombination.clear();
+ m_bSendUpRequired = false;
+}
+
+void AppleRemote::DeInitialize()
+{
+ LOG("uninitializing apple remote\n");
+
+ ResetTimer();
+
+ if (m_socket > 0)
+ close(m_socket);
+
+ m_socket = -1;
+
+ LOG("deleting commands map\n");
+ std::map<std::string, CPacketBUTTON *>::iterator iter = m_mapCommands.begin();
+ while (iter != m_mapCommands.end())
+ {
+ delete iter->second;
+ iter++;
+ }
+ m_mapCommands.clear();
+ m_strCombination.clear();
+}
+
+void AppleRemote::SetTimer()
+{
+ if (m_timer)
+ ResetTimer();
+
+ LOG("setting timer to expire in %.2f secs. \n", m_dMaxClickDuration);
+ CFRunLoopTimerContext context = { 0, this, 0, 0, 0 };
+ m_timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + m_dMaxClickDuration, 0, 0, 0, TimerCallBack, &context);
+ CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_timer, kCFRunLoopCommonModes);
+}
+
+void AppleRemote::ResetTimer()
+{
+ if (m_timer)
+ {
+ LOG("removing timer\n");
+
+ CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), m_timer, kCFRunLoopCommonModes);
+ CFRunLoopTimerInvalidate(m_timer);
+ CFRelease(m_timer);
+ }
+
+ m_timer = NULL;
+}
+
+void AppleRemote::SetMaxClickDuration(double dDuration)
+{
+ m_dMaxClickDuration = dDuration;
+ LOG("setting click max duration to %.2f seconds\n", dDuration);
+}
+
+void AppleRemote::TimerCallBack (CFRunLoopTimerRef timer, void *info)
+{
+ if (!info)
+ {
+ fprintf(stderr, "Error. invalid argument to timer callback\n");
+ return;
+ }
+
+ AppleRemote *pRemote = (AppleRemote *)info;
+ pRemote->SendCommand(pRemote->m_strCombination);
+ pRemote->m_strCombination.clear();
+ pRemote->ResetTimer();
+}
+
+bool AppleRemote::SendCommand(const std::string &key)
+{
+ CPacketBUTTON *pPacket = m_mapCommands[key];
+ if (pPacket)
+ {
+ LOG("Sending command for Key (%s)\n", key.c_str());
+ CAddress addr(m_serverAddress.c_str());
+ pPacket->Send(m_socket, addr);
+
+ if ( 0 == (pPacket->GetFlags() & BTN_NO_REPEAT) )
+ m_bSendUpRequired = true;
+ else
+ m_bSendUpRequired = false;
+ }
+
+ return pPacket != NULL;
+}
+
+void AppleRemote::LaunchApp()
+{
+ // the path to xbmc.app is passed as an arg,
+ // use this to launch from a menu press
+ LOG("Trying to start XBMC: [%s]\n", m_appPath.c_str());
+ if (!m_appPath.empty())
+ {
+ std::string strCmd;
+
+ // build a finder open command
+ strCmd = "XBMC_HOME=";
+ strCmd += m_appHome;
+ strCmd += " ";
+ strCmd += m_appPath;
+ strCmd += " &";
+ LOG("xbmc open command: [%s]\n", strCmd.c_str());
+ system(strCmd.c_str());
+ }
+}
+
+void AppleRemote::SendPacket(CPacketBUTTON &packet)
+{
+ CAddress addr(m_serverAddress.c_str());
+ packet.Send(m_socket, addr);
+}
+
+void AppleRemote::OnKeyDown(const std::string &key)
+{
+ if (!IsProgramRunning(APPLICATION_NAME) && (m_serverAddress == "localhost" || m_serverAddress == "127.0.0.1"))
+ {
+ if (key == m_launch_xbmc_button)
+ LaunchApp();
+
+ return;
+ }
+
+ LOG("key down: %s\n", key.c_str());
+ if (m_remoteMode == REMOTE_NORMAL) {
+ SendCommand(key);
+ }
+ else if (m_remoteMode == REMOTE_UNIVERSAL)
+ {
+ bool bCombinationStart = false;
+ bool bNeedTimer = false;
+ if (m_strCombination.empty())
+ {
+ bNeedTimer = true;
+ for (size_t i=0; i<m_universalPrefixes.size(); i++)
+ {
+ if (m_universalPrefixes[i] == key)
+ {
+ LOG("start of combination (key=%s)\n", key.c_str());
+ bCombinationStart = true;
+ break;
+ }
+ }
+ }
+
+ m_strCombination += key;
+
+ if (bCombinationStart)
+ SetTimer();
+ else
+ {
+ if (SendCommand(m_strCombination))
+ {
+ m_strCombination.clear();
+ ResetTimer();
+ }
+ else if (bNeedTimer)
+ SetTimer();
+ }
+ }
+}
+
+void AppleRemote::OnKeyUp(const std::string &key)
+{
+ LOG("key up: %s\n", key.c_str());
+ if (m_bSendUpRequired)
+ {
+ LOG("sending key-up event\n");
+ CPacketBUTTON btn;
+ CAddress addr(m_serverAddress.c_str());
+ btn.Send(m_socket, addr);
+ }
+ else
+ {
+ LOG("no need to send UP event\n");
+ }
+}
+
+void AppleRemote::SetVerbose(bool bVerbose)
+{
+ m_bVerbose = bVerbose;
+}
+
+bool AppleRemote::IsVerbose()
+{
+ return m_bVerbose;
+}
+
+void AppleRemote::SetMaxClickTimeout(double dTimeout)
+{
+ m_dMaxClickDuration = dTimeout;
+}
+
+void AppleRemote::SetServerAddress(const std::string &strAddress)
+{
+ m_serverAddress = strAddress;
+}
+
+void AppleRemote::SetAppPath(const std::string &strAddress)
+{
+ m_appPath = strAddress;
+}
+
+void AppleRemote::SetAppHome(const std::string &strAddress)
+{
+ m_appHome = strAddress;
+}
+
+const std::string &AppleRemote::GetServerAddress()
+{
+ return m_serverAddress;
+}
+
+void AppleRemote::SetRemoteMode(RemoteModes mode)
+{
+ m_remoteMode = mode;
+}
+
+bool AppleRemote::IsProgramRunning(const char* strProgram, int ignorePid)
+{
+ kinfo_proc* mylist = (kinfo_proc *)malloc(sizeof(kinfo_proc));
+ size_t mycount = 0;
+ bool ret = false;
+
+ GetBSDProcessList(&mylist, &mycount);
+ for(size_t k = 0; k < mycount && ret == false; k++)
+ {
+ kinfo_proc *proc = NULL;
+ proc = &mylist[k];
+
+ //LOG("proc->kp_proc.p_comm: %s\n", proc->kp_proc.p_comm);
+ // Process names are at most sixteen characters long.
+ if (strncmp(proc->kp_proc.p_comm, strProgram, 16) == 0)
+ {
+ if (ignorePid == 0 || ignorePid != proc->kp_proc.p_pid)
+ {
+ //LOG("found: %s\n", proc->kp_proc.p_comm);
+ ret = true;
+ }
+ }
+ }
+ free(mylist);
+ return ret;
+}
+
+int AppleRemote::GetBSDProcessList(kinfo_proc **procList, size_t *procCount)
+ // Returns a list of all BSD processes on the system. This routine
+ // allocates the list and puts it in *procList and a count of the
+ // number of entries in *procCount. You are responsible for freeing
+ // this list (use "free" from System framework).
+ // On success, the function returns 0.
+ // On error, the function returns a BSD errno value.
+{
+ int err;
+ kinfo_proc * result;
+ bool done;
+ static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
+ // Declaring name as const requires us to cast it when passing it to
+ // sysctl because the prototype doesn't include the const modifier.
+ size_t length;
+
+ assert( procList != NULL);
+ assert(procCount != NULL);
+
+ *procCount = 0;
+
+ // We start by calling sysctl with result == NULL and length == 0.
+ // That will succeed, and set length to the appropriate length.
+ // We then allocate a buffer of that size and call sysctl again
+ // with that buffer. If that succeeds, we're done. If that fails
+ // with ENOMEM, we have to throw away our buffer and loop. Note
+ // that the loop causes use to call sysctl with NULL again; this
+ // is necessary because the ENOMEM failure case sets length to
+ // the amount of data returned, not the amount of data that
+ // could have been returned.
+
+ result = NULL;
+ done = false;
+ do {
+ assert(result == NULL);
+
+ // Call sysctl with a NULL buffer.
+
+ length = 0;
+ err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
+ NULL, &length,
+ NULL, 0);
+ if (err == -1) {
+ err = errno;
+ }
+
+ // Allocate an appropriately sized buffer based on the results
+ // from the previous call.
+
+ if (err == 0) {
+ result = (kinfo_proc* )malloc(length);
+ if (result == NULL) {
+ err = ENOMEM;
+ }
+ }
+
+ // Call sysctl again with the new buffer. If we get an ENOMEM
+ // error, toss away our buffer and start again.
+
+ if (err == 0) {
+ err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1,
+ result, &length,
+ NULL, 0);
+ if (err == -1) {
+ err = errno;
+ }
+ if (err == 0) {
+ done = true;
+ } else if (err == ENOMEM) {
+ assert(result != NULL);
+ free(result);
+ result = NULL;
+ err = 0;
+ }
+ }
+ } while (err == 0 && ! done);
+
+ // Clean up and establish post conditions.
+
+ if (err != 0 && result != NULL) {
+ free(result);
+ result = NULL;
+ }
+ *procList = result;
+ if (err == 0) {
+ *procCount = length / sizeof(kinfo_proc);
+ }
+
+ assert( (err == 0) == (*procList != NULL) );
+
+ return err;
+}
+
+int AppleRemote::GetButtonEventTerminator(void)
+{
+ return(m_button_event_terminator);
+}
diff --git a/tools/EventClients/Clients/AppleRemote/AppleRemote.h b/tools/EventClients/Clients/AppleRemote/AppleRemote.h
new file mode 100644
index 0000000000..45e3a427bc
--- /dev/null
+++ b/tools/EventClients/Clients/AppleRemote/AppleRemote.h
@@ -0,0 +1,80 @@
+/*
+ * AppleRemote.h
+ * AppleRemote
+ *
+ *
+ */
+#ifndef __APPLE__REMOTE__H__
+#define __APPLE__REMOTE__H__
+
+#include <ctype.h>
+#include <Carbon/Carbon.h>
+
+#include <vector>
+#include <map>
+#include <string>
+
+typedef enum { REMOTE_NONE, REMOTE_NORMAL, REMOTE_UNIVERSAL } RemoteModes;
+typedef struct kinfo_proc kinfo_proc;
+
+class CPacketBUTTON;
+class AppleRemote
+{
+public:
+ AppleRemote();
+ virtual ~AppleRemote();
+
+ void Initialize();
+ void DeInitialize();
+
+ void ResetTimer();
+ void SetTimer();
+
+ void OnKeyDown(const std::string &key);
+ void OnKeyUp(const std::string &key);
+
+ int GetButtonEventTerminator(void);
+
+ void SetMaxClickDuration(double dDuration);
+
+ void SetVerbose(bool bVerbose);
+ bool IsVerbose();
+
+ void SetMaxClickTimeout(double dTimeout);
+ void SetServerAddress(const std::string &strAddress);
+ void SetAppPath(const std::string &strAddress);
+ void SetAppHome(const std::string &strAddress);
+ void SetRemoteMode(RemoteModes mode);
+
+ const std::string &GetServerAddress();
+
+ static int GetBSDProcessList(kinfo_proc **procList, size_t *procCount);
+ bool IsProgramRunning(const char* strProgram, int ignorePid=0);
+ void LaunchApp();
+
+ void SendPacket(CPacketBUTTON &packet);
+
+protected:
+ static void TimerCallBack (CFRunLoopTimerRef timer, void *info);
+ void RegisterCommand(const std::string &strSequence, CPacketBUTTON *pPacket);
+ bool SendCommand(const std::string &key);
+ void ParseConfig();
+
+ bool m_bVerbose;
+ bool m_bSendUpRequired;
+ RemoteModes m_remoteMode;
+ double m_dMaxClickDuration;
+ int m_socket;
+ std::string m_strCombination;
+ std::string m_serverAddress;
+ std::string m_appPath;
+ std::string m_appHome;
+
+ std::map<std::string, CPacketBUTTON *> m_mapCommands;
+ std::vector<std::string> m_universalPrefixes;
+ std::string m_launch_xbmc_button;
+ int m_button_event_terminator;
+ CFRunLoopTimerRef m_timer;
+};
+
+#endif
diff --git a/tools/EventClients/Clients/AppleRemote/Makefile b/tools/EventClients/Clients/AppleRemote/Makefile
new file mode 100644
index 0000000000..63518afe16
--- /dev/null
+++ b/tools/EventClients/Clients/AppleRemote/Makefile
@@ -0,0 +1,25 @@
+CXX=g++
+CC=g++
+
+CFLAGS+=-isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4
+CFLAGS+=-Wall
+LDFLAGS+=-framework IOKit -framework Carbon -framework ForceFeedback
+
+OBJS = iremoted.o AppleRemote.o
+
+TARGET = ../../../osx/XBMCHelper
+CLEAN_FILES=$(TARGET)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJS)
+ g++ $(LDFLAGS) $(OBJS) -o $(TARGET)
+
+.cpp.o:
+ $(CXX) -c $(CFLAGS) $(DEFINES) $(INCLUDES) $< -o ${<:.cpp=.o}
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(DEFINES) $(INCLUDES) $< -o ${<:.c=.o}
+
+clean:
+ $(RM) -rf *.o ../../../osx/XBMCHelper
diff --git a/tools/EventClients/Clients/AppleRemote/XBox360.h b/tools/EventClients/Clients/AppleRemote/XBox360.h
new file mode 100644
index 0000000000..88d6a0cb94
--- /dev/null
+++ b/tools/EventClients/Clients/AppleRemote/XBox360.h
@@ -0,0 +1,576 @@
+#ifndef __XBOX360_H__
+#define __XBOX360_H__
+
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/hid/IOHIDLib.h>
+#include <IOKit/hid/IOHIDKeys.h>
+#include <IOKit/hid/IOHIDUsageTables.h>
+
+#include <ForceFeedback/ForceFeedback.h>
+
+#include <pthread.h>
+#include <string>
+#include <list>
+
+#include "AppleRemote.h"
+#include "../../lib/c++/xbmcclient.h"
+
+#define SAFELY(expr) if ((expr) != kIOReturnSuccess) { printf("ERROR: \"%s\".\n", #expr); return; }
+
+extern AppleRemote g_appleRemote;
+
+class XBox360Controller
+{
+ public:
+
+ static XBox360Controller* XBox360Controller::Create(io_service_t device, int deviceNum, bool deviceWireless)
+ {
+ XBox360Controller* controller = new XBox360Controller();
+ IOReturn ret;
+ IOCFPlugInInterface **plugInInterface;
+ SInt32 score=0;
+
+ ret = IOCreatePlugInInterfaceForService(device, kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score);
+ if (ret == kIOReturnSuccess)
+ {
+ ret = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID122), (LPVOID*)&controller->hidDevice);
+ (*plugInInterface)->Release(plugInInterface);
+ if (ret == kIOReturnSuccess)
+ {
+ controller->forceFeedback=0;
+ FFCreateDevice(device, &controller->forceFeedback);
+ controller->index = deviceNum;
+ controller->deviceHandle = device;
+ controller->wireless = deviceWireless;
+
+ char str[128];
+ sprintf(str, "%s Controller %d", deviceWireless ? "Wireless" : "Wired", deviceNum);
+ controller->name = str;
+
+ return controller;
+ }
+ }
+
+ return 0;
+ }
+
+ ~XBox360Controller()
+ {
+ if (deviceHandle != 0) IOObjectRelease(deviceHandle);
+ if (hidDevice != 0) (*hidDevice)->Release(hidDevice);
+ if (forceFeedback != 0) FFReleaseDevice(forceFeedback);
+ }
+
+ void start()
+ {
+ int i,j;
+ CFRunLoopSourceRef eventSource;
+
+ // Get serial.
+ FFEFFESCAPE escape;
+ unsigned char c;
+ std::string serial;
+
+ serial = getSerialNumber();
+ //printf("SERIAL: %s, forceFeedback: 0x%08lx\n", serial.c_str(), forceFeedback);
+
+ CFArrayRef elements;
+ SAFELY((*hidDevice)->copyMatchingElements(hidDevice, NULL, &elements));
+
+ for(i=0; i<CFArrayGetCount(elements); i++)
+ {
+ CFDictionaryRef element = (CFDictionaryRef)CFArrayGetValueAtIndex(elements, i);
+ long usage, usagePage, longCookie;
+
+ // Get cookie.
+ if (getVal(element, CFSTR(kIOHIDElementCookieKey), longCookie) &&
+ getVal(element, CFSTR(kIOHIDElementUsageKey), usage) &&
+ getVal(element, CFSTR(kIOHIDElementUsagePageKey), usagePage))
+ {
+ IOHIDElementCookie cookie = (IOHIDElementCookie)longCookie;
+
+ // Match up items
+ switch (usagePage)
+ {
+ case 0x01: // Generic Desktop
+ j=0;
+ switch (usage)
+ {
+ case 0x35: j++; // Right trigger
+ case 0x32: j++; // Left trigger
+ case 0x34: j++; // Right stick Y
+ case 0x33: j++; // Right stick X
+ case 0x31: j++; // Left stick Y
+ case 0x30: // Left stick X
+ axis[j] = cookie;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 0x09: // Button
+ if (usage >= 1 && usage <= 15)
+ {
+ // Button 1-11
+ buttons[usage-1] = cookie;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ // Start queue.
+ SAFELY((*hidDevice)->open(hidDevice, 0));
+
+ hidQueue = (*hidDevice)->allocQueue(hidDevice);
+ if (hidQueue == NULL)
+ {
+ printf("Unable to allocate queue\n");
+ return;
+ }
+
+ // Create queue, set callback.
+ SAFELY((*hidQueue)->create(hidQueue, 0, 32));
+ SAFELY((*hidQueue)->createAsyncEventSource(hidQueue, &eventSource));
+ SAFELY((*hidQueue)->setEventCallout(hidQueue, CallbackFunction, this, NULL));
+
+ // Add to runloop.
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopCommonModes);
+
+ // Add the elements.
+ for(i=0; i<6; i++)
+ (*hidQueue)->addElement(hidQueue, axis[i], 0);
+ for(i=0; i<15; i++)
+ (*hidQueue)->addElement(hidQueue, buttons[i], 0);
+
+ // Start.
+ SAFELY((*hidQueue)->start(hidQueue));
+
+#if 0
+ // Read existing properties
+ {
+// CFDictionaryRef dict=(CFDictionaryRef)IORegistryEntryCreateCFProperty(registryEntry,CFSTR("DeviceData"),NULL,0);
+ CFDictionaryRef dict = (CFDictionaryRef)[GetController(GetSerialNumber(registryEntry)) retain];
+ if(dict!=0) {
+ CFBooleanRef boolValue;
+ CFNumberRef intValue;
+
+ if(CFDictionaryGetValueIfPresent(dict,CFSTR("InvertLeftX"),(void*)&boolValue)) {
+ [leftStickInvertX setState:CFBooleanGetValue(boolValue)?NSOnState:NSOffState];
+ } else NSLog(@"No value for InvertLeftX");
+ if(CFDictionaryGetValueIfPresent(dict,CFSTR("InvertLeftY"),(void*)&boolValue)) {
+ [leftStickInvertY setState:CFBooleanGetValue(boolValue)?NSOnState:NSOffState];
+ } else NSLog(@"No value for InvertLeftY");
+ if(CFDictionaryGetValueIfPresent(dict,CFSTR("RelativeLeft"),(void*)&boolValue)) {
+ BOOL enable=CFBooleanGetValue(boolValue);
+ [leftLinked setState:enable?NSOnState:NSOffState];
+ [leftStick setLinked:enable];
+ } else NSLog(@"No value for RelativeLeft");
+ if(CFDictionaryGetValueIfPresent(dict,CFSTR("DeadzoneLeft"),(void*)&intValue)) {
+ UInt16 i;
+
+ CFNumberGetValue(intValue,kCFNumberShortType,&i);
+ [leftStickDeadzone setIntValue:i];
+ [leftStick setDeadzone:i];
+ } else NSLog(@"No value for DeadzoneLeft");
+ if(CFDictionaryGetValueIfPresent(dict,CFSTR("InvertRightX"),(void*)&boolValue)) {
+ [rightStickInvertX setState:CFBooleanGetValue(boolValue)?NSOnState:NSOffState];
+ } else NSLog(@"No value for InvertRightX");
+ if(CFDictionaryGetValueIfPresent(dict,CFSTR("InvertRightY"),(void*)&boolValue)) {
+ [rightStickInvertY setState:CFBooleanGetValue(boolValue)?NSOnState:NSOffState];
+ } else NSLog(@"No value for InvertRightY");
+ if(CFDictionaryGetValueIfPresent(dict,CFSTR("RelativeRight"),(void*)&boolValue)) {
+ BOOL enable=CFBooleanGetValue(boolValue);
+ [rightLinked setState:enable?NSOnState:NSOffState];
+ [rightStick setLinked:enable];
+ } else NSLog(@"No value for RelativeRight");
+ if(CFDictionaryGetValueIfPresent(dict,CFSTR("DeadzoneRight"),(void*)&intValue)) {
+ UInt16 i;
+
+ CFNumberGetValue(intValue,kCFNumberShortType,&i);
+ [rightStickDeadzone setIntValue:i];
+ [rightStick setDeadzone:i];
+ } else NSLog(@"No value for DeadzoneRight");
+ CFRelease(dict);
+ } else NSLog(@"No settings found");
+ }
+
+ // Set LED and manual motor control
+ // [self updateLED:0x0a];
+ [self setMotorOverride:TRUE];
+ [self testMotorsLarge:0 small:0];
+ largeMotor=0;
+ smallMotor=0;
+
+ // Battery level?
+ {
+ NSBundle *bundle = [NSBundle bundleForClass:[self class]];
+ NSString *path;
+ CFTypeRef prop;
+
+ path = nil;
+ if (IOObjectConformsTo(registryEntry, "WirelessHIDDevice"))
+ {
+ prop = IORegistryEntryCreateCFProperty(registryEntry, CFSTR("BatteryLevel"), NULL, 0);
+ if (prop != nil)
+ {
+ unsigned char level;
+
+ if (CFNumberGetValue(prop, kCFNumberCharType, &level))
+ path = [bundle pathForResource:[NSString stringWithFormat:@"batt%i", level / 64] ofType:@"tif"];
+ CFRelease(prop);
+ }
+ }
+ if (path == nil)
+ path = [bundle pathForResource:@"battNone" ofType:@"tif"];
+ [batteryLevel setImage:[[[NSImage alloc] initByReferencingFile:path] autorelease]];
+ }
+ }
+ #endif
+
+ c = 0x0a;
+ if (serial.length() > 0)
+ {
+ #if 0
+ for (int i = 0; i < 4; i++)
+ {
+ if ((leds[i] == nil) || ([leds[i] caseInsensitiveCompare:serial] == NSOrderedSame))
+ {
+ c = 0x06 + i;
+ if (leds[i] == nil)
+ {
+ //leds[i] = [serial retain];
+ printf("Added controller with LED %i\n", i);
+ }
+ break;
+ }
+ }
+ #endif
+ }
+ c = 0x06;
+ escape.dwSize = sizeof(escape);
+ escape.dwCommand = 0x02;
+ escape.cbInBuffer = sizeof(c);
+ escape.lpvInBuffer = &c;
+ escape.cbOutBuffer = 0;
+ escape.lpvOutBuffer = NULL;
+ if (FFDeviceEscape(forceFeedback, &escape) != FF_OK)
+ printf("Error: FF\n");
+ }
+
+ void stop()
+ {
+ if (hidQueue != 0)
+ {
+ // Stop the queue.
+ (*hidQueue)->stop(hidQueue);
+
+ // Remove from the run loop.
+ CFRunLoopSourceRef eventSource = (*hidQueue)->getAsyncEventSource(hidQueue);
+ if ((eventSource != 0) && CFRunLoopContainsSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopCommonModes))
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopCommonModes);
+
+ // Whack.
+ (*hidQueue)->Release(hidQueue);
+ hidQueue = 0;
+ }
+
+ if (hidDevice != 0)
+ (*hidDevice)->close(hidDevice);
+ }
+
+ std::string getName() { return name; }
+
+ private:
+
+ XBox360Controller()
+ {
+ for (int i=0; i<15; i++)
+ buttons[i] = 0;
+
+ for (int i=0; i<6; i++)
+ axis[i] = 0;
+ }
+
+ std::string getSerialNumber()
+ {
+ CFTypeRef value = IORegistryEntrySearchCFProperty(deviceHandle, kIOServicePlane, CFSTR("SerialNumber"), kCFAllocatorDefault, kIORegistryIterateRecursively);
+ std::string ret = std::string(CFStringGetCStringPtr((CFStringRef)value, kCFStringEncodingMacRoman));
+ CFRelease(value);
+
+ return ret;
+ }
+
+ void updateLED(int ledIndex)
+ {
+ FFEFFESCAPE escape;
+ unsigned char c;
+
+ if (forceFeedback ==0 )
+ return;
+
+ c = ledIndex;
+ escape.dwSize = sizeof(escape);
+ escape.dwCommand = 0x02;
+ escape.cbInBuffer = sizeof(c);
+ escape.lpvInBuffer = &c;
+ escape.cbOutBuffer = 0;
+ escape.lpvOutBuffer = NULL;
+
+ if (FFDeviceEscape(forceFeedback, &escape) != FF_OK)
+ printf("ERROR: FFDeviceEscape.\n");
+ }
+
+ static void CallbackFunction(void* me, IOReturn result, void* refCon, void* sender)
+ {
+ ((XBox360Controller* )me)->handleCallback(result, refCon, sender);
+ }
+
+ void handleCallback(IOReturn result, void* refCon, void* sender)
+ {
+ AbsoluteTime zeroTime={0,0};
+ IOHIDEventStruct event;
+ bool found = false;
+ int i;
+
+ if (sender != hidQueue)
+ return;
+
+ while (result == kIOReturnSuccess)
+ {
+ result = (*hidQueue)->getNextEvent(hidQueue, &event, zeroTime, 0);
+ if (result != kIOReturnSuccess)
+ continue;
+
+ // Check axis
+ for (i=0, found=FALSE; (i<6) && (!found); i++)
+ {
+ if (event.elementCookie == axis[i])
+ {
+ int amount = 0;
+ if (i == 4 || i == 5)
+ amount = event.value * (32768/256) + 32768;
+ else
+ amount = 65536 - (event.value + 32768) - 1;
+
+ // Clip.
+ if (amount > 65535)
+ amount = 65535;
+ if (amount < 0)
+ amount = 0;
+
+ if (g_appleRemote.IsVerbose())
+ printf("Axis %d %d.\n", i+1, amount);
+ CPacketBUTTON btn(i+1, "JS1:Wireless 360 Controller", BTN_AXIS | BTN_NO_REPEAT | BTN_USE_AMOUNT | BTN_QUEUE, amount);
+ g_appleRemote.SendPacket(btn);
+
+ found = true;
+ }
+ }
+
+ if (found) continue;
+
+ // Check buttons
+ for (i=0, found=FALSE; (i<15) && (!found); i++)
+ {
+ if (event.elementCookie == buttons[i])
+ {
+ if (g_appleRemote.IsVerbose())
+ printf("Button: %d %d.\n", i+1, (int)event.value);
+
+ if (i+1 == 11 && !g_appleRemote.IsProgramRunning("XBMC", 0) &&
+ (g_appleRemote.GetServerAddress() == "127.0.0.1" || g_appleRemote.GetServerAddress() == "localhost") &&
+ event.value)
+ {
+ g_appleRemote.LaunchApp();
+ return;
+ }
+
+ int flags = event.value ? BTN_DOWN : BTN_UP;
+ CPacketBUTTON btn(i+1, "JS1:Wireless 360 Controller", flags, 0);
+ g_appleRemote.SendPacket(btn);
+ found = true;
+ }
+ }
+
+ if (found) continue;
+
+ // Cookie wasn't for us?
+ }
+ }
+
+ bool getVal(CFDictionaryRef element, const CFStringRef key, long& value)
+ {
+ CFTypeRef object = CFDictionaryGetValue(element, key);
+
+ if ((object == NULL) || (CFGetTypeID(object) != CFNumberGetTypeID())) return false;
+ if (!CFNumberGetValue((CFNumberRef)object, kCFNumberLongType, &value)) return false;
+
+ return true;
+ }
+
+ IOHIDDeviceInterface122 **hidDevice;
+ FFDeviceObjectReference forceFeedback;
+ io_service_t deviceHandle;
+
+ IOHIDQueueInterface **hidQueue;
+ IOHIDElementCookie axis[6];
+ IOHIDElementCookie buttons[15];
+
+ std::string name;
+ int index;
+ bool wireless;
+};
+
+
+class XBox360
+{
+ public:
+
+ XBox360()
+ {
+ }
+
+ void start()
+ {
+ pthread_create(&_itsThread, NULL, Run, (void *)this);
+ }
+
+ void join()
+ {
+ void* val = 0;
+ pthread_join(_itsThread, &val);
+ }
+
+ protected:
+
+ static void* Run(void* param)
+ {
+ ((XBox360* )param)->run();
+ return 0;
+ }
+
+ void run()
+ {
+ printf("XBOX360: Registering for notifications.\n");
+ io_object_t object;
+
+ // Register for wired notifications.
+ IONotificationPortRef notificationObject = IONotificationPortCreate(kIOMasterPortDefault);
+ IOServiceAddMatchingNotification(notificationObject, kIOFirstMatchNotification, IOServiceMatching(kIOUSBDeviceClassName), callbackHandleDevice, this, &_itsOnIteratorWired);
+ callbackHandleDevice(this, _itsOnIteratorWired);
+
+ IOServiceAddMatchingNotification(notificationObject, kIOTerminatedNotification, IOServiceMatching(kIOUSBDeviceClassName), callbackHandleDevice, this, &_itsOffIteratorWired);
+ while ((object = IOIteratorNext(_itsOffIteratorWired)) != 0)
+ IOObjectRelease(object);
+
+ // Wireless notifications.
+ IOServiceAddMatchingNotification(notificationObject, kIOFirstMatchNotification, IOServiceMatching("WirelessHIDDevice"), callbackHandleDevice, this, &_itsOnIteratorWireless);
+ callbackHandleDevice(this, _itsOnIteratorWireless);
+
+ IOServiceAddMatchingNotification(notificationObject, kIOTerminatedNotification, IOServiceMatching("WirelessHIDDevice"), callbackHandleDevice, this, &_itsOffIteratorWireless);
+ while ((object = IOIteratorNext(_itsOffIteratorWireless)) != 0)
+ IOObjectRelease(object);
+
+ // Add notifications to the run loop.
+ CFRunLoopSourceRef notificationRunLoopSource = IONotificationPortGetRunLoopSource(notificationObject);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), notificationRunLoopSource, kCFRunLoopDefaultMode);
+
+ // Run.
+ CFRunLoopRun();
+ printf("XBOX360: Exiting.\n");
+ }
+
+ private:
+
+ // Callback for Xbox360 device notifications.
+ static void callbackHandleDevice(void* param, io_iterator_t iterator)
+ {
+ io_service_t object = 0;
+ bool update = false;
+
+ while ((object = IOIteratorNext(iterator))!=0)
+ {
+ IOObjectRelease(object);
+ update = true;
+ }
+
+ if (update)
+ ((XBox360* )param)->updateDeviceList();
+ }
+
+ void updateDeviceList()
+ {
+ io_iterator_t iterator;
+ io_object_t hidDevice;
+
+ // Scrub and whack old items.
+ stopDevices();
+
+ for (std::list<XBox360Controller*>::iterator i = controllerList.begin(); i != controllerList.end(); ++i)
+ delete *i;
+ controllerList.clear();
+
+ // Add new items
+ CFMutableDictionaryRef hidDictionary = IOServiceMatching(kIOHIDDeviceKey);
+ IOReturn ioReturn = IOServiceGetMatchingServices(kIOMasterPortDefault, hidDictionary, &iterator);
+
+ if ((ioReturn != kIOReturnSuccess) || (iterator==0))
+ {
+ printf("No devices.\n");
+ return;
+ }
+
+ for (int count = 1; (hidDevice = IOIteratorNext(iterator)); )
+ {
+ bool deviceWired = IOObjectConformsTo(hidDevice, "Xbox360ControllerClass");
+ bool deviceWireless = IOObjectConformsTo(hidDevice, "WirelessHIDDevice");
+
+ if (deviceWired || deviceWireless)
+ {
+ XBox360Controller* controller = XBox360Controller::Create(hidDevice, count, deviceWireless);
+ if (controller)
+ {
+ // Add to list.
+ printf("Adding device: %s.\n", controller->getName().c_str());
+ controllerList.push_back(controller);
+ count++;
+ }
+ }
+ else
+ {
+ IOObjectRelease(hidDevice);
+ }
+ }
+
+ IOObjectRelease(iterator);
+
+ // Make sure all devices are started.
+ startDevices();
+ }
+
+ void stopDevices()
+ {
+ for (std::list<XBox360Controller*>::iterator i = controllerList.begin(); i != controllerList.end(); ++i)
+ (*i)->stop();
+ }
+
+ void startDevices()
+ {
+ for (std::list<XBox360Controller*>::iterator i = controllerList.begin(); i != controllerList.end(); ++i)
+ (*i)->start();
+ }
+
+ std::list<XBox360Controller*> controllerList;
+ pthread_t _itsThread;
+ io_iterator_t _itsOnIteratorWired, _itsOffIteratorWired;
+ io_iterator_t _itsOnIteratorWireless, _itsOffIteratorWireless;
+};
+
+#endif
diff --git a/tools/EventClients/Clients/AppleRemote/iremoted.c b/tools/EventClients/Clients/AppleRemote/iremoted.c
new file mode 100644
index 0000000000..42b3258d03
--- /dev/null
+++ b/tools/EventClients/Clients/AppleRemote/iremoted.c
@@ -0,0 +1,518 @@
+/*
+ * iremoted.c
+ * Display events received from the Apple Infrared Remote.
+ *
+ * gcc -Wall -o iremoted iremoted.c -framework IOKit -framework Carbon
+ *
+ * Copyright (c) 2006-2008 Amit Singh. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define PROGNAME "iremoted"
+#define PROGVERS "2.0"
+
+#include <stdio.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/errno.h>
+#include <sysexits.h>
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/sysctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <mach-o/dyld.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/hid/IOHIDLib.h>
+#include <IOKit/hid/IOHIDKeys.h>
+#include <IOKit/hid/IOHIDUsageTables.h>
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Carbon/Carbon.h>
+
+#include <iostream>
+#include <fstream>
+#include <map>
+#include <string>
+#include <iterator>
+#include <sstream>
+#include <set>
+
+#include "AppleRemote.h"
+#include "XBox360.h"
+
+using namespace std;
+
+void ParseOptions(int argc, char** argv);
+void ReadConfig();
+
+static struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "server", required_argument, 0, 's' },
+ { "universal", no_argument, 0, 'u' },
+ { "multiremote",no_argument, 0, 'm' },
+ { "timeout", required_argument, 0, 't' },
+ { "verbose", no_argument, 0, 'v' },
+ { "externalConfig", no_argument, 0, 'x' },
+ { "appPath", required_argument, 0, 'a' },
+ { "appHome", required_argument, 0, 'z' },
+ { 0, 0, 0, 0 },
+};
+
+static const char *options = "hsumtvxaz";
+
+IOHIDElementCookie buttonNextID = 0;
+IOHIDElementCookie buttonPreviousID = 0;
+
+std::set<IOHIDElementCookie> g_registeredCookies;
+
+AppleRemote g_appleRemote;
+
+void usage(void);
+inline void print_errmsg_if_io_err(int expr, char *msg);
+inline void print_errmsg_if_err(int expr, char *msg);
+void QueueCallbackFunction(void *target, IOReturn result,
+ void *refcon, void *sender);
+bool addQueueCallbacks(IOHIDQueueInterface **hqi);
+void processQueue(IOHIDDeviceInterface **hidDeviceInterface);
+void doRun(IOHIDDeviceInterface **hidDeviceInterface);
+void getHIDCookies(IOHIDDeviceInterface122 **handle);
+void createHIDDeviceInterface(io_object_t hidDevice,
+ IOHIDDeviceInterface ***hdi);
+void setupAndRun(void);
+
+void
+usage(void)
+{
+ printf("%s (version %s)\n", PROGNAME, PROGVERS);
+ printf(" Sends Apple Remote events to XBMC.\n\n");
+ printf("Usage: %s [OPTIONS...]\n\nOptions:\n", PROGNAME);
+ printf(" -h, --help print this help message and exit.\n");
+ printf(" -s, --server <addr> send events to the specified IP.\n");
+ printf(" -u, --universal runs in Universal Remote mode.\n");
+ printf(" -t, --timeout <ms> timeout length for sequences (default: 500ms).\n");
+ printf(" -a, --appPath path to XBMC.app (MenuPress launch support).\n");
+ printf(" -z, --appHome path to XBMC.app/Content/Resources/XBMX \n");
+ printf(" -v, --verbose prints lots of debugging information.\n");
+}
+
+inline void
+print_errmsg_if_io_err(int expr, char *msg)
+{
+ IOReturn err = (expr);
+
+ if (err != kIOReturnSuccess) {
+ fprintf(stderr, "*** %s - %s(%x, %d).\n", msg, mach_error_string(err),
+ err, err & 0xffffff);
+ fflush(stderr);
+ exit(EX_OSERR);
+ }
+}
+
+inline void
+print_errmsg_if_err(int expr, char *msg)
+{
+ if (expr) {
+ fprintf(stderr, "*** %s.\n", msg);
+ fflush(stderr);
+ exit(EX_OSERR);
+ }
+}
+
+void
+QueueCallbackFunction(void *target, IOReturn result, void *refcon, void *sender)
+{
+ HRESULT ret = kIOReturnSuccess;
+ AbsoluteTime zeroTime = {0,0};
+ IOHIDQueueInterface **hqi;
+ IOHIDEventStruct event;
+
+ std::set<int> events;
+ bool bKeyDown = false;
+ while (ret == kIOReturnSuccess) {
+ hqi = (IOHIDQueueInterface **)sender;
+ ret = (*hqi)->getNextEvent(hqi, &event, zeroTime, 0);
+ if (ret != kIOReturnSuccess)
+ continue;
+
+ //printf("%d %d %d\n", (int)event.elementCookie, (int)event.value, (int)event.longValue);
+
+ if (event.value > 0)
+ bKeyDown = true;
+
+ events.insert((int)event.elementCookie);
+ }
+
+ if (events.size() > 1)
+ {
+ std::set<int>::iterator iter = events.find( g_appleRemote.GetButtonEventTerminator() );
+ if (iter != events.end())
+ events.erase(iter);
+ }
+
+ std::string strEvents;
+ std::set<int>::const_iterator iter = events.begin();
+ while (iter != events.end())
+ {
+ //printf("*iter = %d\n", *iter);
+ char strEvent[10];
+ snprintf(strEvent, 10, "%d_", *iter);
+ strEvents += strEvent;
+ iter++;
+ }
+
+ if (bKeyDown)
+ g_appleRemote.OnKeyDown(strEvents);
+ else
+ g_appleRemote.OnKeyUp(strEvents);
+}
+
+bool
+addQueueCallbacks(IOHIDQueueInterface **hqi)
+{
+ IOReturn ret;
+ CFRunLoopSourceRef eventSource;
+ IOHIDQueueInterface ***privateData;
+
+ privateData = (IOHIDQueueInterface ***)malloc(sizeof(*privateData));
+ *privateData = hqi;
+
+ ret = (*hqi)->createAsyncEventSource(hqi, &eventSource);
+ if (ret != kIOReturnSuccess)
+ return false;
+
+ ret = (*hqi)->setEventCallout(hqi, QueueCallbackFunction,
+ NULL, &privateData);
+ if (ret != kIOReturnSuccess)
+ return false;
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), eventSource,
+ kCFRunLoopDefaultMode);
+ return true;
+}
+
+void
+processQueue(IOHIDDeviceInterface **hidDeviceInterface)
+{
+ HRESULT result;
+ IOHIDQueueInterface **queue;
+
+ queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
+ if (!queue) {
+ fprintf(stderr, "Failed to allocate event queue.\n");
+ return;
+ }
+
+ (void)(*queue)->create(queue, 0, 99);
+
+ std::set<IOHIDElementCookie>::const_iterator iter = g_registeredCookies.begin();
+ while (iter != g_registeredCookies.end())
+ {
+ (void)(*queue)->addElement(queue, *iter, 0);
+ iter++;
+ }
+
+ addQueueCallbacks(queue);
+
+ result = (*queue)->start(queue);
+
+ CFRunLoopRun();
+
+ result = (*queue)->stop(queue);
+
+ result = (*queue)->dispose(queue);
+
+ (*queue)->Release(queue);
+}
+
+void
+doRun(IOHIDDeviceInterface **hidDeviceInterface)
+{
+ IOReturn ioReturnValue;
+
+ ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, kIOHIDOptionsTypeSeizeDevice);
+ if (ioReturnValue == kIOReturnExclusiveAccess) {
+ printf("exclusive lock failed\n");
+ }
+
+ processQueue(hidDeviceInterface);
+
+ if (ioReturnValue == KERN_SUCCESS)
+ ioReturnValue = (*hidDeviceInterface)->close(hidDeviceInterface);
+ (*hidDeviceInterface)->Release(hidDeviceInterface);
+}
+
+void
+getHIDCookies(IOHIDDeviceInterface122 **handle)
+{
+ IOHIDElementCookie cookie;
+ CFTypeRef object;
+ long number;
+ long usage;
+ long usagePage;
+ CFArrayRef elements;
+ CFDictionaryRef element;
+ IOReturn result;
+
+ if (!handle || !(*handle))
+ return ;
+
+ result = (*handle)->copyMatchingElements(handle, NULL, &elements);
+
+ if (result != kIOReturnSuccess) {
+ fprintf(stderr, "Failed to copy cookies.\n");
+ exit(1);
+ }
+
+ CFIndex i;
+ for (i = 0; i < CFArrayGetCount(elements); i++) {
+ element = (CFDictionaryRef)CFArrayGetValueAtIndex(elements, i);
+ object = (CFDictionaryGetValue(element, CFSTR(kIOHIDElementCookieKey)));
+ if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
+ continue;
+ if(!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType, &number))
+ continue;
+ cookie = (IOHIDElementCookie)number;
+ object = CFDictionaryGetValue(element, CFSTR(kIOHIDElementUsageKey));
+ if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
+ continue;
+ if (!CFNumberGetValue((CFNumberRef)object, kCFNumberLongType, &number))
+ continue;
+ usage = number;
+ object = CFDictionaryGetValue(element,CFSTR(kIOHIDElementUsagePageKey));
+ if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
+ continue;
+ if (!CFNumberGetValue((CFNumberRef)object, kCFNumberLongType, &number))
+ continue;
+ usagePage = number;
+ if (usagePage == 0x06 && usage == 0x22 ) // event code "39". menu+select for 5 secs
+ g_registeredCookies.insert(cookie);
+ else if (usagePage == 0xff01 && usage == 0x23 ) // event code "35". long click - select
+ g_registeredCookies.insert(cookie);
+ else if (usagePage == kHIDPage_GenericDesktop && usage >= 0x80 && usage <= 0x8d ) // regular keys
+ g_registeredCookies.insert(cookie);
+ else if (usagePage == kHIDPage_Consumer && usage >= 0xB0 && usage <= 0xBF)
+ g_registeredCookies.insert(cookie);
+ else if (usagePage == kHIDPage_Consumer && usage == kHIDUsage_Csmr_Menu ) // event code "18". long "menu"
+ g_registeredCookies.insert(cookie);
+ }
+}
+
+void
+createHIDDeviceInterface(io_object_t hidDevice, IOHIDDeviceInterface ***hdi)
+{
+ io_name_t className;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ HRESULT plugInResult = S_OK;
+ SInt32 score = 0;
+ IOReturn ioReturnValue = kIOReturnSuccess;
+
+ ioReturnValue = IOObjectGetClass(hidDevice, className);
+ print_errmsg_if_io_err(ioReturnValue, "Failed to get class name.");
+
+ ioReturnValue = IOCreatePlugInInterfaceForService(
+ hidDevice,
+ kIOHIDDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface,
+ &score);
+
+ if (ioReturnValue != kIOReturnSuccess)
+ return;
+
+ plugInResult = (*plugInInterface)->QueryInterface(
+ plugInInterface,
+ CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
+ (LPVOID*)hdi);
+ print_errmsg_if_err(plugInResult != S_OK,
+ "Failed to create device interface.\n");
+
+ (*plugInInterface)->Release(plugInInterface);
+}
+
+void
+setupAndRun(void)
+{
+ CFMutableDictionaryRef hidMatchDictionary = NULL;
+ io_service_t hidService = (io_service_t)0;
+ io_object_t hidDevice = (io_object_t)0;
+ IOHIDDeviceInterface **hidDeviceInterface = NULL;
+ IOReturn ioReturnValue = kIOReturnSuccess;
+
+ //hidMatchDictionary = IOServiceNameMatching("AppleIRController");
+ hidMatchDictionary = IOServiceMatching("AppleIRController");
+ hidService = IOServiceGetMatchingService(kIOMasterPortDefault,
+ hidMatchDictionary);
+
+ if (!hidService) {
+ fprintf(stderr, "Apple Infrared Remote not found.\n");
+ exit(1);
+ }
+
+ hidDevice = (io_object_t)hidService;
+
+ createHIDDeviceInterface(hidDevice, &hidDeviceInterface);
+ getHIDCookies((IOHIDDeviceInterface122 **)hidDeviceInterface);
+ ioReturnValue = IOObjectRelease(hidDevice);
+ print_errmsg_if_io_err(ioReturnValue, "Failed to release HID.");
+
+ if (hidDeviceInterface == NULL) {
+ fprintf(stderr, "No HID.\n");
+ exit(1);
+ }
+
+ g_appleRemote.Initialize();
+ doRun(hidDeviceInterface);
+
+ if (ioReturnValue == KERN_SUCCESS)
+ ioReturnValue = (*hidDeviceInterface)->close(hidDeviceInterface);
+
+ (*hidDeviceInterface)->Release(hidDeviceInterface);
+}
+
+void Reconfigure(int nSignal)
+{
+ if (nSignal == SIGHUP) {
+ //fprintf(stderr, "Reconfigure\n");
+ ReadConfig();
+ } else {
+ exit(0);
+ }
+}
+
+void ReadConfig()
+{
+ // Compute filename.
+ string strFile = getenv("HOME");
+ strFile += "/Library/Application Support/XBMC/XBMCHelper.conf";
+
+ // Open file.
+ ifstream ifs(strFile.c_str());
+ if (!ifs)
+ return;
+
+ // Read file.
+ stringstream oss;
+ oss << ifs.rdbuf();
+
+ if (!ifs && !ifs.eof())
+ return;
+
+ // Tokenize.
+ string strData(oss.str());
+ istringstream is(strData);
+ vector<string> args = vector<string>(istream_iterator<string>(is), istream_iterator<string>());
+
+ // Convert to char**.
+ int argc = args.size() + 1;
+ char** argv = new char*[argc + 1];
+ int i = 0;
+ argv[i++] = "XBMCHelper";
+
+ for (vector<string>::iterator it = args.begin(); it != args.end(); )
+ argv[i++] = (char* )(*it++).c_str();
+
+ argv[i] = 0;
+
+ // Parse the arguments.
+ ParseOptions(argc, argv);
+
+ delete[] argv;
+}
+
+void ParseOptions(int argc, char** argv)
+{
+ int c, option_index = 0;
+ bool readExternal = false;
+
+ while ((c = getopt_long(argc, argv, options, long_options, &option_index)) != -1)
+ {
+ switch (c) {
+ case 'h':
+ usage();
+ exit(0);
+ break;
+ case 'v':
+ g_appleRemote.SetVerbose(true);
+ break;
+ case 's':
+ g_appleRemote.SetServerAddress(optarg);
+ break;
+ case 'u':
+ g_appleRemote.SetRemoteMode(REMOTE_UNIVERSAL);
+ break;
+ case 'm':
+ break;
+ case 't':
+ if (optarg)
+ g_appleRemote.SetMaxClickTimeout( atof(optarg) * 0.001 );
+ break;
+ case 'a':
+ g_appleRemote.SetAppPath(optarg);
+ break;
+ case 'z':
+ g_appleRemote.SetAppHome(optarg);
+ break;
+ case 'x':
+ readExternal = true;
+ break;
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+ //reset getopts state
+ optreset = 1;
+ optind = 0;
+
+ if (readExternal == true)
+ ReadConfig();
+}
+
+int
+main (int argc, char **argv)
+{
+ ParseOptions(argc,argv);
+
+ signal(SIGHUP, Reconfigure);
+ signal(SIGINT, Reconfigure);
+ signal(SIGTERM, Reconfigure);
+
+ XBox360 xbox360;
+ xbox360.start();
+
+ setupAndRun();
+
+ xbox360.join();
+
+ return 0;
+}
diff --git a/tools/EventClients/Clients/J2ME Client/README b/tools/EventClients/Clients/J2ME Client/README
new file mode 100644
index 0000000000..ee1426812c
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/README
@@ -0,0 +1,23 @@
+1. Can I use it?
+To use the J2ME client only CLDC 1.0 and MIDP 1.0 is needed.
+The client will also need bluetooth and must be able to initialize the connection.
+
+2. Compiling the client
+To compile the J2ME Application some libraries are needed:
+ *Suns Wireless Toolkit
+ *Java
+ *Ant
+Before it can be compiled the link to Suns Wireless Toolkit are needed and are in build.xml
+under <property name="WTK" value=""> where the value should be the path to the toolkit.
+When compiling in ubuntu all that is needed are the following command
+# ant
+The final jar file will end up in build/release/
+For installations on some J2ME devices a jad file is needed and it needs the correct filesize of the JAR. This is not an
+automated process and thus open the build/release/XBMCRemote.jad and alter it is requiered. The filesize is in bytes
+and reflects that of build/release/XBMCRemote.jar
+
+3. Pre Usage
+To use the EventClient you'll need to run the EventClient on a computer by:
+# python XBMCJ2ME.py
+It depends on Pybluez (http://org.csail.mit.edu/pybluez/) and is available in repository
+# sudo apt-get install python-bluetooth
diff --git a/tools/EventClients/Clients/J2ME Client/build.xml b/tools/EventClients/Clients/J2ME Client/build.xml
new file mode 100644
index 0000000000..4e583b21bc
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/build.xml
@@ -0,0 +1,31 @@
+<project name="XBMC Remote" default="make">
+ <property name="WTK" value=""/>
+ <property name="OUTNAME" value="XBMCRemote"/>
+ <property name="MIDP10_LIB" value="${WTK}/lib/midpapi10.jar"/>
+ <property name="CLDC10_LIB" value="${WTK}/lib/cldcapi10.jar"/>
+ <property name="JSR082_LIB" value="${WTK}/lib/jsr082.jar"/>
+ <property name="ALL_LIB" value="${MIDP10_LIB}:${CLDC10_LIB}:${JSR082_LIB}"/>
+
+ <target name="compile">
+ <tstamp/>
+ <mkdir dir="build/compiled"/>
+ <javac destdir="build/compiled" srcdir="src" bootclasspath="${ALL_LIB}" target="1.1"/>
+ </target>
+
+ <target name="preverify" depends="compile">
+ <mkdir dir="build/preverified"/>
+ <exec executable="${WTK}/bin/preverify">
+ <arg line="-classpath ${ALL_LIB}"/>
+ <arg line="-d build/preverified"/>
+ <arg line="build/compiled"/>
+ </exec>
+ </target>
+
+ <target name="make" depends="preverify">
+ <mkdir dir="build/release"/>
+ <jar basedir="build/preverified" jarfile="build/release/${OUTNAME}.jar" manifest="manifest.mf">
+ <fileset dir="icons"/>
+ </jar>
+ <copy file="src/XBMCRemote.jad" tofile="build/release/${OUTNAME}.jad"/>
+ </target>
+</project>
diff --git a/tools/EventClients/Clients/J2ME Client/icons/icon.png b/tools/EventClients/Clients/J2ME Client/icons/icon.png
new file mode 100644
index 0000000000..812237b3ac
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/icons/icon.png
Binary files differ
diff --git a/tools/EventClients/Clients/J2ME Client/j2me_remote.py b/tools/EventClients/Clients/J2ME Client/j2me_remote.py
new file mode 100755
index 0000000000..5f78f83ffd
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/j2me_remote.py
@@ -0,0 +1,175 @@
+#!/usr/bin/python
+#
+# XBMC Media Center
+# J2ME Remote
+# Copyright (c) 2008 topfs2
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+import sys
+try:
+ from xbmc.xbmcclient import *
+ from xbmc.bt.bt import *
+ from xbmc.defs import *
+except:
+ sys.path.append('../../lib/python')
+ from xbmcclient import *
+ from bt.bt import *
+ ICON_PATH = "../../icons/"
+
+from socket import *
+import os
+from threading import Thread
+
+host = "localhost"
+port = 9777
+addr = (host, port)
+sock = socket(AF_INET,SOCK_DGRAM)
+
+def send_key(key):
+ packet = PacketBUTTON(map_name="JS0:J2ME", code=key, repeat=0, queue=0)
+ packet.send(sock, addr)
+
+def send_message(caption, msg):
+ packet = PacketNOTIFICATION(caption,
+ msg,
+ ICON_PNG,
+ icon_file=ICON_PATH + "/phone.png")
+
+ packet.send(sock, addr)
+
+def send_ImageToBluetooth(Image):
+ imageData = file( Image ).read()
+ print "Data len ", len( imageData )
+ client_sock.send( format.uint32( len( imageData) ) )
+ client_sock.sendall(imageData)
+ print "Sent file"
+
+class Ping(Thread):
+ def __init__ (self):
+ Thread.__init__(self)
+ def run(self):
+ import time
+ while 1: # ping the server every 19s
+ packet = PacketPING()
+ packet.send(sock, addr)
+ time.sleep (19)
+
+def main():
+ import time
+
+ # connect to localhost, port 9777 using a UDP socket
+ # this only needs to be done once.
+ # by default this is where XBMC will be listening for incoming
+ # connections.
+ server_sock=bt_create_rfcomm_socket()
+ server_sock.listen(2)
+
+ portBT = server_sock.getsockname()[1]
+
+ uuid = "ACDC"
+ bt_advertise(socket=server_sock, name="XBMC Remote", uuid=uuid)
+
+ print "Waiting for connection on RFCOMM channel %d" % portBT
+
+ packet = PacketHELO(devicename="J2ME Remote",
+ icon_type=ICON_PNG,
+ icon_file=ICON_PATH + "/phone.png")
+ packet.send(sock, addr)
+ Ping().start()
+ while(True):
+ try:
+ client_sock, client_info = server_sock.accept()
+ print "Accepted connection from ", client_info
+ client_addr, client_port = client_info
+ runFlag = True
+ firstRun = True
+ print "Accepted connection from ", client_addr
+ except KeyboardInterrupt:
+ packet = PacketLOG(LOGNOTICE, "J2ME: User interrupted")
+ packet.send(sock, addr)
+ bt_stop_advertising(socket = server_sock)
+ server_sock.close()
+ sys.exit(0)
+ try:
+ while runFlag:
+ if firstRun:
+ client_name = client_sock.recv(1024)
+ firstRun = False
+ send_message("J2ME: Phone Connected", client_name)
+ packet = PacketLOG(LOGNOTICE, "Phone Connected")
+ packet.send(sock, addr)
+ data = client_sock.recv(3)
+ if(data[0] == "0"):
+ if(data[1] == "0"):
+ runFlag = False
+ packet = PacketLOG(LOGNOTICE, "J2ME: Recieved disconnect command")
+ packet.send(sock, addr)
+ client_sock.close()
+ if(data[1] == "1"):
+ print "Shuting down server"
+ client_sock.close()
+ bt_stop_advertising(socket = server_sock)
+ server_sock.close()
+ packet = PacketBYE()
+ packet.send(sock, addr)
+ sys.exit(0)
+
+ elif(data[0] == "2"):
+ print "<button id=\"%i\">" % ord( data[1] )
+ send_key(ord( data[1] ) )
+ else:
+ print "Unkown received data [%s]" % data
+ except IOError:
+ print "Lost connection too %s" % client_name
+ runFlag = False
+ send_message("Phone Disconnected", client_name)
+ pass
+ except KeyboardInterrupt:
+ packet = PacketLOG(LOGNOTICE, "J2ME: User interrupted")
+ packet.send(sock, addr)
+ client_sock.close()
+ bt_stop_advertising(socket = server_sock)
+ server_sock.close()
+ packet = PacketBYE() # PacketPING if you want to ping
+ packet.send(sock, addr)
+ sys.exit(0)
+
+def usage():
+ print """
+Java Bluetooth Phone Remote Control Client for XBMC v0.1
+
+Usage for xbmcphone.py
+ --address ADDRESS (default: localhost)
+ --port PORT (default: 9777)
+ --help (Brings up this message)
+"""
+
+if __name__=="__main__":
+ try:
+ i = 0
+ while (i < len(sys.argv)):
+ if (sys.argv[i] == "--address"):
+ host = sys.argv[i + 1]
+ elif (sys.argv[i] == "--port"):
+ port = sys.argv[i + 1]
+ elif (sys.argv[i] == "--help"):
+ usage()
+ sys.exit(0)
+ i = i + 1
+ main()
+ except:
+ sys.exit(0)
diff --git a/tools/EventClients/Clients/J2ME Client/manifest.mf b/tools/EventClients/Clients/J2ME Client/manifest.mf
new file mode 100644
index 0000000000..49cddf5c28
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/manifest.mf
@@ -0,0 +1,7 @@
+MIDlet-1: MainScreen, , MainScreen
+MIDlet-Vendor: team-xbmc
+MIDlet-Name: XBMC Remote
+MIDlet-Icon: icon.png
+MIDlet-Version: 0.1
+MicroEdition-Configuration: CLDC-1.0
+MicroEdition-Profile: MIDP-1.0
diff --git a/tools/EventClients/Clients/J2ME Client/src/BluetoothCommunicator.java b/tools/EventClients/Clients/J2ME Client/src/BluetoothCommunicator.java
new file mode 100644
index 0000000000..2db3f54dd2
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/src/BluetoothCommunicator.java
@@ -0,0 +1,228 @@
+/*
+* XBMC Media Center
+* UDP Event Server
+* Copyright (c) 2008 topfs2
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+import javax.microedition.io.Connector;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import javax.microedition.io.StreamConnection;
+import java.lang.NullPointerException;
+import java.util.Vector;
+import javax.bluetooth.*;
+import javax.bluetooth.LocalDevice;
+import javax.microedition.lcdui.Form;
+import javax.microedition.lcdui.Image;
+import javax.microedition.lcdui.Display;
+import java.lang.*;
+
+public class BluetoothCommunicator extends Thread
+{
+ private boolean Run;
+ private Form Log;
+ private BluetoothEvent recvEvent;
+
+ protected static String Address = null;
+ private static StreamConnection Connection = null;
+ private static DataInputStream inputStream = null;
+ private static DataOutputStream outputStream = null;
+
+ public BluetoothCommunicator(BluetoothEvent recvEvent, Form Log)
+ {
+ this.recvEvent = recvEvent;
+ this.Log = Log;
+ }
+
+ public static boolean Connect(Form Log, String addr)
+ {
+ Log.append("Going to connect\n");
+ try
+ {
+ Connection = (StreamConnection)Connector.open(addr);
+
+ if (Connection == null)
+ {
+ Log.append("Connector.open returned null\n");
+ return false;
+ }
+ else
+ {
+ Log.append("Connector.open returned connection\n");
+ inputStream = Connection.openDataInputStream();
+ outputStream = Connection.openDataOutputStream();
+ Address = addr;
+ return true;
+ }
+ }
+ catch (Exception e)
+ {
+ Log.append("Exception" + e.getMessage() + "\n");
+ try
+ {
+ if (Connection != null)
+ Connection.close();
+ }
+ catch (Exception ee)
+ {
+ Log.append("Exception " + ee.getMessage() + "\n");
+ }
+ Connection = null;
+ return false;
+ }
+ }
+
+ public static boolean Handshake(Form Log, Display disp)
+ {
+ if (Connection == null)
+ return false;
+ try
+ {
+ LocalDevice localDevice = LocalDevice.getLocalDevice();
+ Send(localDevice.getFriendlyName());
+
+ return true;
+ }
+ catch(NullPointerException NPE)
+ {
+ Log.append("Null " + NPE.getMessage());
+ }
+ catch (IndexOutOfBoundsException IOBE)
+ {
+ Log.append("IndexOutOfBounds " + IOBE.getMessage());
+ }
+ catch (IllegalArgumentException IAE)
+ {
+ Log.append("illegalArg " + IAE.getMessage());
+ }
+ catch(Exception e)
+ {
+ Log.append("Exception in handshake " + e.getMessage());
+ }
+ return false;
+ }
+ public void run()
+ {
+ Run = true;
+ while (Run)
+ {
+ try
+ {
+ if (inputStream.available() <= 0)
+ sleep(10);
+ else
+ {
+ Log.append("run:Ava " + inputStream.available() + "\n");
+ byte []b = new byte[1];
+ b[0] = (byte)255; // safe thing for now
+ inputStream.read(b, 0, 1);
+ byte[] recv = null;
+ switch (b[0])
+ {
+ case BluetoothEvent.RECV_SYSTEM:
+ recv = new byte[]{ inputStream.readByte() };
+ break;
+ case BluetoothEvent.RECV_COMMAND:
+ recv = new byte[]{ inputStream.readByte() };
+ break;
+ case BluetoothEvent.RECV_IMAGE:
+ Log.append("Recv_Image\n");
+ recv = RecvImage(Log);
+ break;
+ }
+ if (recv != null)
+ recvEvent.Recv(b[0], recv);
+ }
+ }
+ catch (Exception e)
+ {
+ Log.append("RecvException " + e.getMessage());
+ }
+ }
+ }
+ public void close()
+ {
+ Run = false;
+ }
+ public static byte[] RecvImage(Form Log)
+ {
+ try
+ {
+ int length = inputStream.readInt();
+ Log.append("readUnsignedShort: " + length + "\n");
+ byte[] arrayToUse = new byte[length];
+ inputStream.readFully(arrayToUse, 0, length);
+ return arrayToUse;
+ }
+ catch (Exception e )
+ {
+ Log.append("Exception RecvImage " + e.getMessage());
+ return null;
+ }
+ }
+
+ public static int Recv(byte[] arrayToUse, Form Log )
+ {
+ if (Connection == null)
+ return -1;
+ try
+ {
+ return inputStream.read(arrayToUse);
+ }
+ catch (Exception e)
+ {
+ Log.append("Exception in Recv " + e.getMessage());
+ return -1;
+ }
+ }
+
+ public static boolean Send(byte[] packet)
+ {
+ if (Connection == null)
+ return false;
+ try
+ {
+ outputStream.write(packet, 0, packet.length);
+ return true;
+ }
+ catch (Exception e)
+ {
+ return false;
+ }
+ }
+
+ public static boolean Send(String packet)
+ {
+ return Send(packet.getBytes());
+ }
+
+ public static void Disconnect()
+ {
+ if (Connection == null)
+ return;
+
+ try
+ {
+ byte[] packet = { '0', '0' };
+ Send(packet);
+ Connection.close();
+ }
+ catch (Exception e)
+ { }
+
+ Connection = null;
+ }
+}
diff --git a/tools/EventClients/Clients/J2ME Client/src/BluetoothEvent.java b/tools/EventClients/Clients/J2ME Client/src/BluetoothEvent.java
new file mode 100644
index 0000000000..6557dca2e0
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/src/BluetoothEvent.java
@@ -0,0 +1,27 @@
+/*
+* XBMC Media Center
+* UDP Event Server
+* Copyright (c) 2008 topfs2
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+public interface BluetoothEvent {
+ public final int RECV_SYSTEM = 0;
+ public final int RECV_COMMAND = 1;
+ public final int RECV_IMAGE = 2;
+
+ // The header is a byte IRL. 0 - 255 and tells packet type
+ public void Recv(int Header, byte[] Payload);
+}
diff --git a/tools/EventClients/Clients/J2ME Client/src/BluetoothServiceDiscovery.java b/tools/EventClients/Clients/J2ME Client/src/BluetoothServiceDiscovery.java
new file mode 100644
index 0000000000..3ff71f13f2
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/src/BluetoothServiceDiscovery.java
@@ -0,0 +1,126 @@
+/*
+* XBMC Media Center
+* UDP Event Server
+* Copyright (c) 2008 topfs2
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+import java.util.Vector;
+import javax.bluetooth.DeviceClass;
+import javax.bluetooth.DiscoveryAgent;
+import javax.bluetooth.DiscoveryListener;
+import javax.bluetooth.LocalDevice;
+import javax.bluetooth.RemoteDevice;
+import javax.bluetooth.ServiceRecord;
+import javax.bluetooth.UUID;
+
+import javax.microedition.lcdui.*;
+
+public class BluetoothServiceDiscovery implements DiscoveryListener
+{
+ private static Object lock=new Object();
+ private static Vector vecDevices=new Vector();
+ private static String connectionURL=null;
+
+ public static String Scan(Form Log, String ScanForUUID) throws Exception
+ {
+ BluetoothServiceDiscovery bluetoothServiceDiscovery=new BluetoothServiceDiscovery();
+ //display local device address and name
+ LocalDevice localDevice = LocalDevice.getLocalDevice();
+ //find devices
+ DiscoveryAgent agent = localDevice.getDiscoveryAgent();
+
+ agent.startInquiry(DiscoveryAgent.GIAC, bluetoothServiceDiscovery);
+ try
+ {
+ synchronized(lock)
+ { lock.wait(); }
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ Log.append("Device Inquiry Completed. \n");
+
+ int deviceCount=vecDevices.size();
+ if(deviceCount <= 0)
+ {
+ Log.append("No Devices Found .\n");
+ return null;
+ }
+ else
+ {
+ Log.append("Bluetooth Devices: \n");
+ for (int i = 0; i <deviceCount; i++)
+ {
+ RemoteDevice remoteDevice=(RemoteDevice)vecDevices.elementAt(i);
+ String t = i + ": \"" + remoteDevice.getFriendlyName(true) + "\"";
+ Log.append(t + "\n");
+ }
+ }
+ UUID[] uuidSet = new UUID[1];
+ if (ScanForUUID != null)
+ uuidSet[0]=new UUID(ScanForUUID, true);
+ else
+ uuidSet[0]=new UUID("ACDC", true);
+
+ for (int i = 0; i < vecDevices.capacity(); i++)
+ {
+ RemoteDevice remoteDevice=(RemoteDevice)vecDevices.elementAt(i);
+ String t = remoteDevice.getFriendlyName(true);
+ agent.searchServices(null,uuidSet,remoteDevice,bluetoothServiceDiscovery);
+ try
+ {
+ synchronized(lock)
+ { lock.wait(); }
+ }
+ catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+ if(connectionURL != null)
+ return connectionURL;
+ }
+ Log.append("Couldn't find any computer that were suitable\n");
+ return null;
+ }
+
+ public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod)
+ {
+ if(!vecDevices.contains(btDevice))
+ vecDevices.addElement(btDevice);
+ }
+
+ public void servicesDiscovered(int transID, ServiceRecord[] servRecord)
+ {
+ if(servRecord!=null && servRecord.length>0){
+ connectionURL=servRecord[0].getConnectionURL(0,false);
+ }
+ synchronized(lock)
+ { lock.notify(); }
+ }
+
+ public void serviceSearchCompleted(int transID, int respCode)
+ {
+ synchronized(lock)
+ { lock.notify(); }
+ }
+
+ public void inquiryCompleted(int discType)
+ {
+ synchronized(lock)
+ { lock.notify(); }
+ }
+}
diff --git a/tools/EventClients/Clients/J2ME Client/src/KeyCanvas.java b/tools/EventClients/Clients/J2ME Client/src/KeyCanvas.java
new file mode 100644
index 0000000000..8c2153c0d6
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/src/KeyCanvas.java
@@ -0,0 +1,66 @@
+/*
+* XBMC Media Center
+* UDP Event Server
+* Copyright (c) 2008 topfs2
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+import javax.microedition.lcdui.*;
+
+public class KeyCanvas extends Canvas
+{
+ private KeyHandler m_SendKey = null;
+ private Image Cover = null;
+ private boolean SetCover = false;
+
+ public KeyCanvas(KeyHandler sendKey)
+ {
+ m_SendKey = sendKey;
+ try
+ {
+// Cover = Image.createImage("home.png");
+ }
+ catch (Exception e)
+ { }
+ }
+
+ public void SetCover(Image newCover)
+ {
+ if (newCover != null)
+ {
+ SetCover = true;
+ Cover = newCover;
+ }
+ }
+
+ public void paint(Graphics g)
+ {
+ g.drawRect(0, 0, this.getWidth(), this.getHeight());
+ if (Cover != null)
+ g.drawImage(Cover, 0, 0, g.TOP|g.LEFT);
+ }
+
+ public void keyPressed(int keyCode)
+ {
+ if (m_SendKey != null)
+ m_SendKey.keyPressed(keyCode);
+ }
+
+ public void keyRepeated(int keyCode)
+ {
+ if (m_SendKey != null)
+ m_SendKey.keyPressed(keyCode);
+ }
+}
diff --git a/tools/EventClients/Clients/J2ME Client/src/KeyHandler.java b/tools/EventClients/Clients/J2ME Client/src/KeyHandler.java
new file mode 100644
index 0000000000..a6a5ac10d0
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/src/KeyHandler.java
@@ -0,0 +1,23 @@
+/*
+* XBMC Media Center
+* UDP Event Server
+* Copyright (c) 2008 topfs2
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+public interface KeyHandler
+{
+ public void keyPressed(int keyCode);
+}
diff --git a/tools/EventClients/Clients/J2ME Client/src/MainScreen.java b/tools/EventClients/Clients/J2ME Client/src/MainScreen.java
new file mode 100644
index 0000000000..2e68c7d211
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/src/MainScreen.java
@@ -0,0 +1,271 @@
+/*
+* XBMC Media Center
+* UDP Event Server
+* Copyright (c) 2008 topfs2
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+import java.util.Vector;
+import javax.bluetooth.DeviceClass;
+import javax.bluetooth.DiscoveryAgent;
+import javax.bluetooth.DiscoveryListener;
+import javax.bluetooth.LocalDevice;
+import javax.bluetooth.RemoteDevice;
+import javax.bluetooth.ServiceRecord;
+import javax.bluetooth.UUID;
+
+import javax.microedition.midlet.*;
+import javax.microedition.lcdui.*;
+
+public class MainScreen extends MIDlet implements CommandListener, KeyHandler, BluetoothEvent
+{
+ private boolean LogActive = true;
+ private final int VersionMajor = 0;
+ private final int VersionMinor = 1;
+ private final String Version = (VersionMajor + "." + VersionMinor);
+
+ private final Command CMD_OK = new Command("OK", Command.OK, 1);
+ private final Command CMD_CONNECT = new Command("Connect", Command.OK, 1);
+ private final Command CMD_DISCONNECT = new Command("Disconnect", Command.CANCEL, 1);
+ private final Command CMD_EXIT = new Command("Exit", Command.CANCEL, 1);
+ private final Command CMD_LOG = new Command("Log", Command.OK, 1);
+ private final Command CMD_SHOWREMOTE = new Command("Remote", Command.OK, 1);
+ private Display display;
+ private Form MainForm;
+
+ private KeyCanvas keyHandler;
+ private BluetoothCommunicator RecvThread;
+
+ private final int CONNECTED = 1;
+ private final int NOT_CONNECTED = -1;
+ private final int CONNECTING = 0;
+
+ private int Connect = NOT_CONNECTED;
+
+ public MainScreen()
+ {
+ display = Display.getDisplay(this);
+ keyHandler = new KeyCanvas(this);
+ keyHandler.addCommand(CMD_LOG);
+ keyHandler.addCommand(CMD_DISCONNECT);
+ keyHandler.setCommandListener(this);
+ }
+
+
+ public void Disconnect()
+ {
+ RecvThread.close();
+ BluetoothCommunicator.Disconnect();
+ MainForm.removeCommand(CMD_DISCONNECT);
+ MainForm.removeCommand(CMD_SHOWREMOTE);
+ MainForm.addCommand(CMD_CONNECT);
+ Connect = NOT_CONNECTED;
+ }
+
+ public void Connect()
+ {
+ if (Connect == CONNECTED)
+ return;
+ Connect = CONNECTING;
+ MainForm = CreateForm("Connect log");
+ display.setCurrent(MainForm);
+ try
+ {
+ String Connectable = BluetoothServiceDiscovery.Scan(MainForm, "ACDC");
+ if (Connectable != null)
+ {
+ MainForm.append("\n" + Connectable);
+ display.setCurrent(MainForm);
+ if (BluetoothCommunicator.Connect(MainForm, Connectable))
+ Connect = CONNECTED;
+ else
+ Connect = NOT_CONNECTED;
+
+ display.setCurrent(MainForm);
+ BluetoothCommunicator.Handshake(MainForm, display);
+ RecvThread = new BluetoothCommunicator(this, MainForm);
+
+ display.setCurrent(MainForm);
+ }
+ }
+ catch (Exception e)
+ {
+ MainForm.append("Error: Couldn't Search");
+ Connect = NOT_CONNECTED;
+ display.setCurrent(MainForm);
+ return;
+ }
+
+ if (Connect == CONNECTED)
+ {
+ MainForm.addCommand(CMD_SHOWREMOTE);
+ MainForm.addCommand(CMD_DISCONNECT);
+ MainForm.append("Connected to a server now we start the receive thread\n");
+ RecvThread.start();
+ }
+ else
+ {
+ MainForm.addCommand(CMD_CONNECT);
+ MainForm.addCommand(CMD_EXIT);
+ MainForm.append("Not connected to a server");
+ }
+ setCurrent();
+ }
+
+ public void startApp()
+ {
+ int length = ((255 << 8) | (255));
+ System.out.println("Len " + length);
+
+
+ MainForm = CreateForm("XBMC Remote");
+ MainForm.append("Hi and welcome to the version " + Version + " of XBMC Remote.\nPressing Connect will have this remote connect to the first available Server");
+ setCurrent();
+ }
+
+ private Form CreateForm(String Title)
+ {
+ Form rtnForm = new Form(Title);
+ if (Connect == CONNECTED)
+ rtnForm.addCommand(CMD_OK);
+ else if (Connect == NOT_CONNECTED)
+ rtnForm.addCommand(CMD_CONNECT);
+
+ if (Connect == CONNECTED)
+ rtnForm.addCommand(CMD_DISCONNECT);
+ else if (Connect == NOT_CONNECTED)
+ rtnForm.addCommand(CMD_EXIT);
+
+ rtnForm.setCommandListener(this);
+ return rtnForm;
+ }
+
+ private void setCurrent()
+ {
+ setCurrent(false);
+ }
+ private void setCurrent(boolean forceLog)
+ {
+ if (forceLog)
+ display.setCurrent(MainForm);
+ else if (Connect == CONNECTED)
+ {
+ display.setCurrent(keyHandler);
+ keyHandler.repaint();
+ }
+ else
+ {
+ display.setCurrent(MainForm);
+ }
+ }
+
+ public void pauseApp()
+ {
+ }
+
+ public void destroyApp(boolean unconditional)
+ {
+ if (Connect == CONNECTED)
+ {
+ BluetoothCommunicator.Disconnect();
+ RecvThread.close();
+ }
+ }
+
+ public void commandAction(Command c, Displayable d)
+ {
+ if (c.equals(CMD_LOG))
+ setCurrent(true);
+ else if (c.equals(CMD_SHOWREMOTE))
+ {
+ display.setCurrent(keyHandler);
+ keyHandler.repaint();
+ }
+ else if (d.equals(MainForm))
+ {
+ if (c.equals(CMD_OK))
+ {
+ byte[] Packet = {'1', '1'}; // 48 == '0'
+ BluetoothCommunicator.Send(Packet);
+ }
+ else if (c.equals(CMD_CONNECT))
+ {
+ this.Connect();
+ }
+ else if (c.equals(CMD_EXIT))
+ System.out.println("QUIT");
+ else if (c.equals(CMD_DISCONNECT))
+ {
+ this.Disconnect();
+ this.RecvThread.close();
+ setCurrent();
+ }
+ }
+ else if (d.equals(keyHandler))
+ {
+ if (c.equals(CMD_OK))
+ {
+ byte[] Packet = {'2', (byte)251 };
+ BluetoothCommunicator.Send(Packet);
+ }
+ else if (c.equals(CMD_DISCONNECT))
+ {
+ this.Disconnect();
+ Connect = NOT_CONNECTED;
+ setCurrent();
+ }
+ }
+ }
+
+ public void keyPressed(int keyCode)
+ {
+ byte[] Packet = {'2', (byte)keyCode};
+ if (keyCode >= Canvas.KEY_NUM0 && keyCode <= Canvas.KEY_NUM9)
+ Packet[1] = (byte)keyCode;
+
+ else if (keyCode == Canvas.UP || keyCode == -1)
+ Packet[1] = 'U';
+ else if (keyCode == Canvas.DOWN || keyCode == -2)
+ Packet[1] = 'D';
+ else if (keyCode == Canvas.LEFT || keyCode == -3)
+ Packet[1] = 'L';
+ else if (keyCode == Canvas.RIGHT || keyCode == -4)
+ Packet[1] = 'R';
+
+ else if (keyCode == Canvas.KEY_STAR)
+ Packet[1] = '*';
+ else if (keyCode == Canvas.KEY_POUND)
+ Packet[1] = '#';
+
+ BluetoothCommunicator.Send(Packet);
+ System.out.println("got" + keyCode + " I'll send [" + Packet[0] + "," + Packet[1] + "]");
+ }
+ public void Recv(int Header, byte[] Payload)
+ {
+ switch(Header)
+ {
+ case BluetoothEvent.RECV_COMMAND:
+ break;
+ case BluetoothEvent.RECV_SYSTEM:
+ if (Payload[0] == 0)
+ this.Disconnect();
+ break;
+ case BluetoothEvent.RECV_IMAGE:
+ keyHandler.SetCover(Image.createImage(Payload, 0, Payload.length));
+ break;
+ }
+ }
+}
diff --git a/tools/EventClients/Clients/J2ME Client/src/XBMCRemote.jad b/tools/EventClients/Clients/J2ME Client/src/XBMCRemote.jad
new file mode 100644
index 0000000000..bd283d8903
--- /dev/null
+++ b/tools/EventClients/Clients/J2ME Client/src/XBMCRemote.jad
@@ -0,0 +1,9 @@
+MIDlet-1: MainScreen, , MainScreen
+MIDlet-Icon: icon.png
+MIDlet-Jar-Size: 12153
+MIDlet-Jar-URL: XBMC Remote.jar
+MIDlet-Name: XBMC Remote
+MIDlet-Vendor: team-xbmc
+MIDlet-Version: 0.1
+MicroEdition-Configuration: CLDC-1.0
+MicroEdition-Profile: MIDP-1.0
diff --git a/tools/EventClients/Clients/OSXRemote/Makefile.in b/tools/EventClients/Clients/OSXRemote/Makefile.in
new file mode 100644
index 0000000000..4b66f568db
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/Makefile.in
@@ -0,0 +1,18 @@
+#convenience makefile to allow headless osx build
+
+DEBUG_FLAGS=@DEBUG_FLAGS@
+
+#check for debug build
+ifneq (,$(findstring _DEBUG, $(DEBUG_FLAGS)))
+ CONFIGURATION=Debug
+else
+ CONFIGURATION=Release
+endif
+
+all:
+ xcodebuild -sdk macosx10.4 -configuration $(CONFIGURATION)
+
+clean:
+ xcodebuild clean -configuration $(CONFIGURATION)
+
+.PHONY: all clean
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/AppleRemote.h b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/AppleRemote.h
new file mode 100644
index 0000000000..4bb2f0998d
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/AppleRemote.h
@@ -0,0 +1,44 @@
+/*****************************************************************************
+ * RemoteControlWrapper.h
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import "HIDRemoteControlDevice.h"
+
+/* Interacts with the Apple Remote Control HID device
+ The class is not thread safe
+ */
+@interface AppleRemote : HIDRemoteControlDevice {
+ int deviceID;
+}
+
+//AppleRemote supports different device IDs (as long they are not paired)
+//the deviceID of last sent button can be queried with - (int) deviceID
+- (int) deviceID;
+
+//overwritten from baseclass to get notified of device changes
+- (eCookieModifier) handleCookie: (long)f_cookie value:(int) f_value;
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/AppleRemote.m b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/AppleRemote.m
new file mode 100644
index 0000000000..152e755aa5
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/AppleRemote.m
@@ -0,0 +1,108 @@
+/*****************************************************************************
+ * RemoteControlWrapper.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import "AppleRemote.h"
+
+#import <mach/mach.h>
+#import <mach/mach_error.h>
+#import <IOKit/IOKitLib.h>
+#import <IOKit/IOCFPlugIn.h>
+#import <IOKit/hid/IOHIDKeys.h>
+
+const char* AppleRemoteDeviceName = "AppleIRController";
+
+// the WWDC 07 Leopard Build is missing the constant
+#ifndef NSAppKitVersionNumber10_4
+#define NSAppKitVersionNumber10_4 824
+#endif
+
+@implementation AppleRemote
+
++ (const char*) remoteControlDeviceName {
+ return AppleRemoteDeviceName;
+}
+
+- (void) setCookieMappingInDictionary: (NSMutableDictionary*) _cookieToButtonMapping {
+ if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_4) {
+ // 10.4.x Tiger
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlus] forKey:@"14_12_11_6_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMinus] forKey:@"14_13_11_6_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu] forKey:@"14_7_6_14_7_6_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay] forKey:@"14_8_6_14_8_6_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight] forKey:@"14_9_6_14_9_6_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft] forKey:@"14_10_6_14_10_6_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold] forKey:@"14_6_4_2_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold] forKey:@"14_6_3_2_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold] forKey:@"14_6_14_6_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Hold] forKey:@"18_14_6_18_14_6_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched] forKey:@"19_"];
+ } else {
+ // 10.5.x Leopard
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlus] forKey:@"31_29_28_19_18_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMinus] forKey:@"31_30_28_19_18_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu] forKey:@"31_20_19_18_31_20_19_18_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay] forKey:@"31_21_19_18_31_21_19_18_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight] forKey:@"31_22_19_18_31_22_19_18_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft] forKey:@"31_23_19_18_31_23_19_18_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold] forKey:@"31_19_18_4_2_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold] forKey:@"31_19_18_3_2_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold] forKey:@"31_19_18_31_19_18_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Hold] forKey:@"35_31_19_18_35_31_19_18_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched] forKey:@"19_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched] forKey:@"39_"];
+ }
+}
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown {
+ if (pressedDown == NO && event == kRemoteButtonMenu_Hold) {
+ // There is no seperate event for pressed down on menu hold. We are simulating that event here
+ [super sendRemoteButtonEvent:event pressedDown:YES];
+ }
+
+ [super sendRemoteButtonEvent:event pressedDown:pressedDown];
+
+ if (pressedDown && (event == kRemoteButtonRight || event == kRemoteButtonLeft || event == kRemoteButtonPlay || event == kRemoteButtonMenu || event == kRemoteButtonPlay_Hold)) {
+ // There is no seperate event when the button is being released. We are simulating that event here
+ [super sendRemoteButtonEvent:event pressedDown:NO];
+ }
+}
+
+- (eCookieModifier) handleCookie: (long)f_cookie value:(int) f_value {
+ switch(f_cookie)
+ {
+ case 39:
+ deviceID = f_value;
+ return PASS_COOKIE;
+ default:
+ return [super handleCookie:f_cookie value:f_value];
+ }
+}
+
+- (int) deviceID {
+ return deviceID;
+}
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/GlobalKeyboardDevice.h b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/GlobalKeyboardDevice.h
new file mode 100644
index 0000000000..bcb11b4245
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/GlobalKeyboardDevice.h
@@ -0,0 +1,49 @@
+/*****************************************************************************
+ * GlobalKeyboardDevice.h
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import <Carbon/Carbon.h>
+
+#import "RemoteControl.h"
+
+/*
+ This class registers for a number of global keyboard shortcuts to simulate a remote control
+ */
+@interface GlobalKeyboardDevice : RemoteControl {
+
+ NSMutableDictionary* hotKeyRemoteEventMapping;
+ EventHandlerRef eventHandlerRef;
+
+}
+
+- (void) mapRemoteButton: (RemoteControlEventIdentifier) remoteButtonIdentifier defaultKeycode: (unsigned int) defaultKeycode defaultModifiers: (unsigned int) defaultModifiers;
+
+- (BOOL)registerHotKeyCode: (unsigned int) keycode modifiers: (unsigned int) modifiers remoteEventIdentifier: (RemoteControlEventIdentifier) identifier;
+
+static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* refCon );
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/GlobalKeyboardDevice.m b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/GlobalKeyboardDevice.m
new file mode 100644
index 0000000000..f568d1f1a3
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/GlobalKeyboardDevice.m
@@ -0,0 +1,241 @@
+/*****************************************************************************
+ * GlobalKeyboardDevice.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+
+#import "GlobalKeyboardDevice.h"
+
+#define F1 122
+#define F2 120
+#define F3 99
+#define F4 118
+#define F5 96
+#define F6 97
+#define F7 98
+
+/*
+ the following default keys are read and shall be used to change the keyboard mapping
+
+ mac.remotecontrols.GlobalKeyboardDevice.plus_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.plus_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.minus_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.minus_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.play_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.play_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.left_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.left_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.right_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.right_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.menu_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.menu_keycode
+ mac.remotecontrols.GlobalKeyboardDevice.playhold_modifiers
+ mac.remotecontrols.GlobalKeyboardDevice.playhold_keycode
+ */
+
+
+@implementation GlobalKeyboardDevice
+
+- (id) initWithDelegate: (id) _remoteControlDelegate {
+ if (self = [super initWithDelegate: _remoteControlDelegate]) {
+ hotKeyRemoteEventMapping = [[NSMutableDictionary alloc] init];
+
+ unsigned int modifiers = cmdKey + shiftKey /*+ optionKey*/ + controlKey;
+
+ [self mapRemoteButton:kRemoteButtonPlus defaultKeycode:F1 defaultModifiers:modifiers];
+ [self mapRemoteButton:kRemoteButtonMinus defaultKeycode:F2 defaultModifiers:modifiers];
+ [self mapRemoteButton:kRemoteButtonPlay defaultKeycode:F3 defaultModifiers:modifiers];
+ [self mapRemoteButton:kRemoteButtonLeft defaultKeycode:F4 defaultModifiers:modifiers];
+ [self mapRemoteButton:kRemoteButtonRight defaultKeycode:F5 defaultModifiers:modifiers];
+ [self mapRemoteButton:kRemoteButtonMenu defaultKeycode:F6 defaultModifiers:modifiers];
+ [self mapRemoteButton:kRemoteButtonPlay_Hold defaultKeycode:F7 defaultModifiers:modifiers];
+ }
+ return self;
+}
+
+- (void) dealloc {
+ [hotKeyRemoteEventMapping release];
+ [super dealloc];
+}
+
+- (void) mapRemoteButton: (RemoteControlEventIdentifier) remoteButtonIdentifier defaultKeycode: (unsigned int) defaultKeycode defaultModifiers: (unsigned int) defaultModifiers {
+ NSString* defaultsKey;
+
+ switch(remoteButtonIdentifier) {
+ case kRemoteButtonPlus:
+ defaultsKey = @"plus";
+ break;
+ case kRemoteButtonMinus:
+ defaultsKey = @"minus";
+ break;
+ case kRemoteButtonMenu:
+ defaultsKey = @"menu";
+ break;
+ case kRemoteButtonPlay:
+ defaultsKey = @"play";
+ break;
+ case kRemoteButtonRight:
+ defaultsKey = @"right";
+ break;
+ case kRemoteButtonLeft:
+ defaultsKey = @"left";
+ break;
+ case kRemoteButtonPlay_Hold:
+ defaultsKey = @"playhold";
+ break;
+ default:
+ NSLog(@"Unknown global keyboard defaults key for remote button identifier %d", remoteButtonIdentifier);
+ }
+
+ NSNumber* modifiersCfg = [[NSUserDefaults standardUserDefaults] objectForKey: [NSString stringWithFormat: @"mac.remotecontrols.GlobalKeyboardDevice.%@_modifiers", defaultsKey]];
+ NSNumber* keycodeCfg = [[NSUserDefaults standardUserDefaults] objectForKey: [NSString stringWithFormat: @"mac.remotecontrols.GlobalKeyboardDevice.%@_keycode", defaultsKey]];
+
+ unsigned int modifiers = defaultModifiers;
+ if (modifiersCfg) modifiers = [modifiersCfg unsignedIntValue];
+
+ unsigned int keycode = defaultKeycode;
+ if (keycodeCfg) keycode = [keycodeCfg unsignedIntValue];
+
+ [self registerHotKeyCode: keycode modifiers: modifiers remoteEventIdentifier: remoteButtonIdentifier];
+}
+
+- (void) setListeningToRemote: (BOOL) value {
+ if (value == [self isListeningToRemote]) return;
+ if (value) {
+ [self startListening: self];
+ } else {
+ [self stopListening: self];
+ }
+}
+- (BOOL) isListeningToRemote {
+ return (eventHandlerRef!=NULL);
+}
+
+- (IBAction) startListening: (id) sender {
+
+ if (eventHandlerRef) return;
+
+ EventTypeSpec eventSpec[2] = {
+ { kEventClassKeyboard, kEventHotKeyPressed },
+ { kEventClassKeyboard, kEventHotKeyReleased }
+ };
+
+ InstallEventHandler( GetEventDispatcherTarget(),
+ (EventHandlerProcPtr)hotKeyEventHandler,
+ 2, eventSpec, self, &eventHandlerRef);
+}
+- (IBAction) stopListening: (id) sender {
+ RemoveEventHandler(eventHandlerRef);
+ eventHandlerRef = NULL;
+}
+
+- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier {
+ NSEnumerator* values = [hotKeyRemoteEventMapping objectEnumerator];
+ NSNumber* remoteIdentifier;
+ while( (remoteIdentifier = [values nextObject]) ) {
+ if ([remoteIdentifier unsignedIntValue] == identifier) return YES;
+ }
+ return NO;
+}
+
++ (const char*) remoteControlDeviceName {
+ return "Keyboard";
+}
+
+- (BOOL)registerHotKeyCode: (unsigned int) keycode modifiers: (unsigned int) modifiers remoteEventIdentifier: (RemoteControlEventIdentifier) identifier {
+ OSStatus err;
+ EventHotKeyID hotKeyID;
+ EventHotKeyRef carbonHotKey;
+
+ hotKeyID.signature = 'PTHk';
+ hotKeyID.id = (long)keycode;
+
+ err = RegisterEventHotKey(keycode, modifiers, hotKeyID, GetEventDispatcherTarget(), (int)nil, &carbonHotKey );
+
+ if( err )
+ return NO;
+
+ [hotKeyRemoteEventMapping setObject: [NSNumber numberWithInt:identifier] forKey: [NSNumber numberWithUnsignedInt: hotKeyID.id]];
+
+ return YES;
+}
+/*
+ - (void)unregisterHotKey: (PTHotKey*)hotKey
+ {
+ OSStatus err;
+ EventHotKeyRef carbonHotKey;
+ NSValue* key;
+
+ if( [[self allHotKeys] containsObject: hotKey] == NO )
+ return;
+
+ carbonHotKey = [self _carbonHotKeyForHotKey: hotKey];
+ NSAssert( carbonHotKey != nil, @"" );
+
+ err = UnregisterEventHotKey( carbonHotKey );
+ //Watch as we ignore 'err':
+
+ key = [NSValue valueWithPointer: carbonHotKey];
+ [mHotKeys removeObjectForKey: key];
+
+ [self _updateEventHandler];
+
+ //See that? Completely ignored
+ }
+ */
+
+- (RemoteControlEventIdentifier) remoteControlEventIdentifierForID: (unsigned int) id {
+ NSNumber* remoteEventIdentifier = [hotKeyRemoteEventMapping objectForKey:[NSNumber numberWithUnsignedInt: id]];
+ return [remoteEventIdentifier unsignedIntValue];
+}
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown {
+ [delegate sendRemoteButtonEvent: event pressedDown: pressedDown remoteControl:self];
+}
+
+static RemoteControlEventIdentifier lastEvent;
+
+static OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void* userData )
+{
+ GlobalKeyboardDevice* keyboardDevice = (GlobalKeyboardDevice*) userData;
+ EventHotKeyID hkCom;
+ GetEventParameter(inEvent,kEventParamDirectObject,typeEventHotKeyID,NULL,sizeof(hkCom),NULL,&hkCom);
+
+ RemoteControlEventIdentifier identifier = [keyboardDevice remoteControlEventIdentifierForID:hkCom.id];
+ if (identifier == 0) return noErr;
+
+ BOOL pressedDown = YES;
+ if (identifier != lastEvent) {
+ lastEvent = identifier;
+ } else {
+ lastEvent = 0;
+ pressedDown = NO;
+ }
+ [keyboardDevice sendRemoteButtonEvent: identifier pressedDown: pressedDown];
+
+ return noErr;
+}
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/HIDRemoteControlDevice.h b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/HIDRemoteControlDevice.h
new file mode 100644
index 0000000000..1bd3dff60a
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/HIDRemoteControlDevice.h
@@ -0,0 +1,77 @@
+/*****************************************************************************
+ * HIDRemoteControlDevice.h
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import <Carbon/Carbon.h>
+#import <IOKit/hid/IOHIDLib.h>
+
+#import "RemoteControl.h"
+
+typedef enum _eCookieModifier{
+ DISCARD_COOKIE = 0,
+ PASS_COOKIE
+} eCookieModifier;
+
+/*
+ Base class for HID based remote control devices
+ */
+@interface HIDRemoteControlDevice : RemoteControl {
+ IOHIDDeviceInterface** hidDeviceInterface;
+ IOHIDQueueInterface** queue;
+ NSMutableArray* allCookies;
+ NSMutableDictionary* cookieToButtonMapping;
+ CFRunLoopSourceRef eventSource;
+
+ BOOL fixSecureEventInputBug;
+ BOOL openInExclusiveMode;
+ BOOL processesBacklog;
+
+ int supportedButtonEvents;
+
+ EventHandlerUPP appSwitchedHandlerUPP;
+ EventHandlerRef appSwitchedHandlerRef;
+}
+
+// When your application needs to much time on the main thread when processing an event other events
+// may already be received which are put on a backlog. As soon as your main thread
+// has some spare time this backlog is processed and may flood your delegate with calls.
+// Backlog processing is turned off by default.
+- (BOOL) processesBacklog;
+- (void) setProcessesBacklog: (BOOL) value;
+
+// methods that should be overwritten by subclasses
+- (void) setCookieMappingInDictionary: (NSMutableDictionary*) cookieToButtonMapping;
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown;
+
+//overwrite this to be able to interact with cookie handling
+//by default (f_cookie == 5) is discarded
+- (eCookieModifier) handleCookie: (long)f_cookie value:(int) f_value;
+
++ (BOOL) isRemoteAvailable;
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/HIDRemoteControlDevice.m b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/HIDRemoteControlDevice.m
new file mode 100644
index 0000000000..5fa61c46b5
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/HIDRemoteControlDevice.m
@@ -0,0 +1,547 @@
+/*****************************************************************************
+ * HIDRemoteControlDevice.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import "HIDRemoteControlDevice.h"
+
+#import <mach/mach.h>
+#import <mach/mach_error.h>
+#import <IOKit/IOKitLib.h>
+#import <IOKit/IOCFPlugIn.h>
+#import <IOKit/hid/IOHIDKeys.h>
+#import "XBMCDebugHelpers.h"
+
+@interface HIDRemoteControlDevice (PrivateMethods)
+- (NSDictionary*) cookieToButtonMapping;
+- (IOHIDQueueInterface**) queue;
+- (IOHIDDeviceInterface**) hidDeviceInterface;
+- (void) removeNotifcationObserver;
+- (void) remoteControlAvailable:(NSNotification *)notification;
+- (void) enableSecureInputFix;
+- (void) disableSecureInputFix;
+@end
+
+@interface HIDRemoteControlDevice (IOKitMethods)
++ (io_object_t) findRemoteDevice;
+- (IOHIDDeviceInterface**) createInterfaceForDevice: (io_object_t) hidDevice;
+- (BOOL) initializeCookies;
+- (BOOL) openDevice;
+@end
+
+//handler to regrab device if openend in exclusive mode on app-switches
+//installed in startListening and removed in stopListening
+pascal OSStatus appSwitchedEventHandler(EventHandlerCallRef nextHandler,
+ EventRef switchEvent,
+ void* userData);
+
+@implementation HIDRemoteControlDevice
+
++ (const char*) remoteControlDeviceName {
+ return "";
+}
+
++ (BOOL) isRemoteAvailable {
+ io_object_t hidDevice = [self findRemoteDevice];
+ if (hidDevice != 0) {
+ IOObjectRelease(hidDevice);
+ return YES;
+ } else {
+ return NO;
+ }
+}
+
+- (id) initWithDelegate: (id) _remoteControlDelegate {
+ if ([[self class] isRemoteAvailable] == NO) return nil;
+
+ if ( self = [super initWithDelegate: _remoteControlDelegate] ) {
+ openInExclusiveMode = YES;
+ queue = NULL;
+ hidDeviceInterface = NULL;
+ cookieToButtonMapping = [[NSMutableDictionary alloc] init];
+
+ [self setCookieMappingInDictionary: cookieToButtonMapping];
+
+ NSEnumerator* enumerator = [cookieToButtonMapping objectEnumerator];
+ NSNumber* identifier;
+ supportedButtonEvents = 0;
+ while(identifier = [enumerator nextObject]) {
+ supportedButtonEvents |= [identifier intValue];
+ }
+
+ fixSecureEventInputBug = [[NSUserDefaults standardUserDefaults] boolForKey: @"remoteControlWrapperFixSecureEventInputBug"];
+ appSwitchedHandlerUPP = NewEventHandlerUPP(appSwitchedEventHandler);
+ }
+
+ return self;
+}
+
+- (void) dealloc {
+ [self removeNotifcationObserver];
+ [self stopListening:self];
+ [cookieToButtonMapping release];
+ [super dealloc];
+}
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown {
+ [delegate sendRemoteButtonEvent: event pressedDown: pressedDown remoteControl:self];
+}
+
+- (void) setCookieMappingInDictionary: (NSMutableDictionary*) cookieToButtonMapping {
+}
+- (int) remoteIdSwitchCookie {
+ return 0;
+}
+
+- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier {
+ return (supportedButtonEvents & identifier) == identifier;
+}
+
+- (BOOL) isListeningToRemote {
+ return (hidDeviceInterface != NULL && allCookies != NULL && queue != NULL);
+}
+
+- (void) setListeningToRemote: (BOOL) value {
+ if (value == NO) {
+ [self stopListening:self];
+ } else {
+ [self startListening:self];
+ }
+}
+
+- (BOOL) isOpenInExclusiveMode {
+ return openInExclusiveMode;
+}
+- (void) setOpenInExclusiveMode: (BOOL) value {
+ openInExclusiveMode = value;
+}
+
+- (BOOL) processesBacklog {
+ return processesBacklog;
+}
+- (void) setProcessesBacklog: (BOOL) value {
+ processesBacklog = value;
+}
+
+//handler to regrab device if openend in exclusive mode on app-switches
+//installed in startListening and removed in stopListening
+pascal OSStatus appSwitchedEventHandler(EventHandlerCallRef nextHandler,
+ EventRef switchEvent,
+ void* userData)
+{
+ HIDRemoteControlDevice* p_this = (HIDRemoteControlDevice*)userData;
+ if (p_this->hidDeviceInterface)
+ {
+ if ((*(p_this->hidDeviceInterface))->close(p_this->hidDeviceInterface) != KERN_SUCCESS)
+ ELOG("failed closing Apple Remote device\n");
+
+ if ((*(p_this->hidDeviceInterface))->open(p_this->hidDeviceInterface, kIOHIDOptionsTypeSeizeDevice) != KERN_SUCCESS)
+ ELOG("failed opening Apple Remote device\n");
+ }
+ //DLOG("Reopened Apple Remote in exclusive mode\n");
+ return 0;
+}
+
+- (IBAction) startListening: (id) sender {
+ if ([self isListeningToRemote]) return;
+
+ // 4th July 2007
+ //
+ // A security update in february of 2007 introduced an odd behavior.
+ // Whenever SecureEventInput is activated or deactivated the exclusive access
+ // to the remote control device is lost. This leads to very strange behavior where
+ // a press on the Menu button activates FrontRow while your app still gets the event.
+ // A great number of people have complained about this.
+ //
+ // Enabling the SecureEventInput and keeping it enabled does the trick.
+ //
+ // I'm pretty sure this is a kind of bug at Apple and I'm in contact with the responsible
+ // Apple Engineer. This solution is not a perfect one - I know.
+ // One of the side effects is that applications that listen for special global keyboard shortcuts (like Quicksilver)
+ // may get into problems as they no longer get the events.
+ // As there is no official Apple Remote API from Apple I also failed to open a technical incident on this.
+ //
+ // Note that there is a corresponding DisableSecureEventInput in the stopListening method below.
+ //
+ [self enableSecureInputFix];
+
+ [self removeNotifcationObserver];
+
+ io_object_t hidDevice = [[self class] findRemoteDevice];
+ if (hidDevice == 0) return;
+
+ if ([self createInterfaceForDevice:hidDevice] == NULL) {
+ goto error;
+ }
+
+ if ([self initializeCookies]==NO) {
+ goto error;
+ }
+
+ if ([self openDevice]==NO) {
+ goto error;
+ }
+ // be KVO friendly
+ [self willChangeValueForKey:@"listeningToRemote"];
+ [self didChangeValueForKey:@"listeningToRemote"];
+ goto cleanup;
+
+error:
+ [self stopListening:self];
+ [self disableSecureInputFix];
+
+cleanup:
+ IOObjectRelease(hidDevice);
+}
+
+- (IBAction) stopListening: (id) sender {
+ if ([self isListeningToRemote]==NO) return;
+
+ BOOL sendNotification = NO;
+
+ if (eventSource != NULL) {
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
+ CFRelease(eventSource);
+ eventSource = NULL;
+ }
+ if (queue != NULL) {
+ (*queue)->stop(queue);
+
+ //dispose of queue
+ (*queue)->dispose(queue);
+
+ //release the queue we allocated
+ (*queue)->Release(queue);
+
+ queue = NULL;
+
+ sendNotification = YES;
+ }
+
+ if (allCookies != nil) {
+ [allCookies autorelease];
+ allCookies = nil;
+ }
+
+ if (hidDeviceInterface != NULL) {
+ //close the device
+ (*hidDeviceInterface)->close(hidDeviceInterface);
+
+ //release the interface
+ (*hidDeviceInterface)->Release(hidDeviceInterface);
+
+ hidDeviceInterface = NULL;
+ }
+ [self disableSecureInputFix];
+
+ if ([self isOpenInExclusiveMode] && sendNotification) {
+ [[self class] sendFinishedNotifcationForAppIdentifier: nil];
+ }
+ // be KVO friendly
+ [self willChangeValueForKey:@"listeningToRemote"];
+ [self didChangeValueForKey:@"listeningToRemote"];
+}
+
+- (eCookieModifier) handleCookie: (long)f_cookie value:(int) f_value {
+ if(f_cookie == 5)
+ return DISCARD_COOKIE;
+ else
+ return PASS_COOKIE;
+}
+
+@end
+
+@implementation HIDRemoteControlDevice (PrivateMethods)
+
+- (IOHIDQueueInterface**) queue {
+ return queue;
+}
+
+- (IOHIDDeviceInterface**) hidDeviceInterface {
+ return hidDeviceInterface;
+}
+
+
+- (NSDictionary*) cookieToButtonMapping {
+ return cookieToButtonMapping;
+}
+
+- (void) removeNotifcationObserver {
+ [[NSDistributedNotificationCenter defaultCenter] removeObserver:self name:FINISHED_USING_REMOTE_CONTROL_NOTIFICATION object:nil];
+}
+
+- (void) remoteControlAvailable:(NSNotification *)notification {
+ [self removeNotifcationObserver];
+ [self startListening: self];
+}
+
+- (void) enableSecureInputFix {
+ if ([self isOpenInExclusiveMode]) {
+ //2 ways to fix exclusive mode, either the one mentioned above
+ if (fixSecureEventInputBug)
+ EnableSecureEventInput();
+ else {
+ //or registering for all app-switch events and just reopening the device on each switch
+ const EventTypeSpec applicationEvents[] = { { kEventClassApplication, kEventAppFrontSwitched } };
+ InstallApplicationEventHandler(appSwitchedHandlerUPP,
+ GetEventTypeCount(applicationEvents), applicationEvents, self, &appSwitchedHandlerRef);
+ }
+ }
+}
+
+- (void) disableSecureInputFix{
+ if ([self isOpenInExclusiveMode]){
+ if(fixSecureEventInputBug)
+ DisableSecureEventInput();
+ else{
+ RemoveEventHandler(appSwitchedHandlerRef);
+ }
+ }
+}
+
+@end
+
+/* Callback method for the device queue
+ Will be called for any event of any type (cookie) to which we subscribe
+ */
+static void QueueCallbackFunction(void* target, IOReturn result, void* refcon, void* sender) {
+ if (target < 0) {
+ NSLog(@"QueueCallbackFunction called with invalid target!");
+ return;
+ }
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+ HIDRemoteControlDevice* remote = (HIDRemoteControlDevice*)target;
+ IOHIDEventStruct event;
+ AbsoluteTime zeroTime = {0,0};
+ NSMutableString* cookieString = [NSMutableString stringWithCapacity: 10];
+ SInt32 sumOfValues = 0;
+ NSNumber* lastSubButtonId = nil;
+ while (result == kIOReturnSuccess)
+ {
+ result = (*[remote queue])->getNextEvent([remote queue], &event, zeroTime, 0);
+ if ( result != kIOReturnSuccess )
+ continue;
+
+ //printf("%d %d %d\n", event.elementCookie, event.value, event.longValue);
+ if([remote handleCookie:(long)event.elementCookie value:event.value] != DISCARD_COOKIE){
+ sumOfValues+=event.value;
+ [cookieString appendString:[NSString stringWithFormat:@"%d_", event.elementCookie]];
+ }
+ //check if this is a valid button
+ NSNumber* buttonId = [[remote cookieToButtonMapping] objectForKey: cookieString];
+ if (buttonId != nil){
+ if([remote processesBacklog]) {
+ //send the button
+ [remote sendRemoteButtonEvent: [buttonId intValue] pressedDown: (sumOfValues>0)];
+ //reset
+ } else {
+ //store button for later use
+ lastSubButtonId = buttonId;
+ }
+ sumOfValues = 0;
+ cookieString = [NSMutableString stringWithCapacity: 10];
+ }
+ }
+ if ([remote processesBacklog] == NO && lastSubButtonId != nil) {
+ // process the last event of the backlog and assume that the button is not pressed down any longer.
+ // The events in the backlog do not seem to be in order and therefore (in rare cases) the last event might be
+ // a button pressed down event while in reality the user has released it.
+ // NSLog(@"processing last event of backlog");
+ [remote sendRemoteButtonEvent: [lastSubButtonId intValue] pressedDown: (sumOfValues>0)];
+ }
+ if ([cookieString length] > 0) {
+ NSLog(@"Unknown button for cookiestring %@", cookieString);
+ }
+ [pool release];
+}
+
+@implementation HIDRemoteControlDevice (IOKitMethods)
+
+- (IOHIDDeviceInterface**) createInterfaceForDevice: (io_object_t) hidDevice {
+ io_name_t className;
+ IOCFPlugInInterface** plugInInterface = NULL;
+ HRESULT plugInResult = S_OK;
+ SInt32 score = 0;
+ IOReturn ioReturnValue = kIOReturnSuccess;
+
+ hidDeviceInterface = NULL;
+
+ ioReturnValue = IOObjectGetClass(hidDevice, className);
+
+ if (ioReturnValue != kIOReturnSuccess) {
+ NSLog(@"Error: Failed to get class name.");
+ return NULL;
+ }
+
+ ioReturnValue = IOCreatePlugInInterfaceForService(hidDevice,
+ kIOHIDDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface,
+ &score);
+ if (ioReturnValue == kIOReturnSuccess)
+ {
+ //Call a method of the intermediate plug-in to create the device interface
+ plugInResult = (*plugInInterface)->QueryInterface(plugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (LPVOID) &hidDeviceInterface);
+
+ if (plugInResult != S_OK) {
+ NSLog(@"Error: Couldn't create HID class device interface");
+ }
+ // Release
+ if (plugInInterface) (*plugInInterface)->Release(plugInInterface);
+ }
+ return hidDeviceInterface;
+}
+
+- (BOOL) initializeCookies {
+ IOHIDDeviceInterface122** handle = (IOHIDDeviceInterface122**)hidDeviceInterface;
+ IOHIDElementCookie cookie;
+ long usage;
+ long usagePage;
+ id object;
+ NSArray* elements = nil;
+ NSDictionary* element;
+ IOReturn success;
+
+ if (!handle || !(*handle)) return NO;
+
+ // Copy all elements, since we're grabbing most of the elements
+ // for this device anyway, and thus, it's faster to iterate them
+ // ourselves. When grabbing only one or two elements, a matching
+ // dictionary should be passed in here instead of NULL.
+ success = (*handle)->copyMatchingElements(handle, NULL, (CFArrayRef*)&elements);
+
+ if (success == kIOReturnSuccess) {
+
+ [elements autorelease];
+ /*
+ cookies = calloc(NUMBER_OF_APPLE_REMOTE_ACTIONS, sizeof(IOHIDElementCookie));
+ memset(cookies, 0, sizeof(IOHIDElementCookie) * NUMBER_OF_APPLE_REMOTE_ACTIONS);
+ */
+ allCookies = [[NSMutableArray alloc] init];
+
+ NSEnumerator *elementsEnumerator = [elements objectEnumerator];
+
+ while (element = [elementsEnumerator nextObject]) {
+ //Get cookie
+ object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementCookieKey) ];
+ if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
+ if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID()) continue;
+ cookie = (IOHIDElementCookie) [object longValue];
+
+ //Get usage
+ object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementUsageKey) ];
+ if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
+ usage = [object longValue];
+
+ //Get usage page
+ object = [element valueForKey: (NSString*)CFSTR(kIOHIDElementUsagePageKey) ];
+ if (object == nil || ![object isKindOfClass:[NSNumber class]]) continue;
+ usagePage = [object longValue];
+
+ [allCookies addObject: [NSNumber numberWithInt:(int)cookie]];
+ }
+ } else {
+ return NO;
+ }
+
+ return YES;
+}
+
+- (BOOL) openDevice {
+ HRESULT result;
+
+ IOHIDOptionsType openMode = kIOHIDOptionsTypeNone;
+ if ([self isOpenInExclusiveMode]) openMode = kIOHIDOptionsTypeSeizeDevice;
+ IOReturn ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, openMode);
+
+ if (ioReturnValue == KERN_SUCCESS) {
+ queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
+ if (queue) {
+ result = (*queue)->create(queue, 0, 12); //depth: maximum number of elements in queue before oldest elements in queue begin to be lost.
+
+ IOHIDElementCookie cookie;
+ NSEnumerator *allCookiesEnumerator = [allCookies objectEnumerator];
+
+ while (cookie = (IOHIDElementCookie)[[allCookiesEnumerator nextObject] intValue]) {
+ (*queue)->addElement(queue, cookie, 0);
+ }
+
+ // add callback for async events
+ ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource);
+ if (ioReturnValue == KERN_SUCCESS) {
+ ioReturnValue = (*queue)->setEventCallout(queue,QueueCallbackFunction, self, NULL);
+ if (ioReturnValue == KERN_SUCCESS) {
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), eventSource, kCFRunLoopDefaultMode);
+
+ //start data delivery to queue
+ (*queue)->start(queue);
+ return YES;
+ } else {
+ NSLog(@"Error when setting event callback");
+ }
+ } else {
+ NSLog(@"Error when creating async event source");
+ }
+ } else {
+ NSLog(@"Error when opening device");
+ }
+ } else if (ioReturnValue == kIOReturnExclusiveAccess) {
+ // the device is used exclusive by another application
+
+ // 1. we register for the FINISHED_USING_REMOTE_CONTROL_NOTIFICATION notification
+ [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(remoteControlAvailable:) name:FINISHED_USING_REMOTE_CONTROL_NOTIFICATION object:nil];
+
+ // 2. send a distributed notification that we wanted to use the remote control
+ [[self class] sendRequestForRemoteControlNotification];
+ }
+ return NO;
+}
+
++ (io_object_t) findRemoteDevice {
+ CFMutableDictionaryRef hidMatchDictionary = NULL;
+ IOReturn ioReturnValue = kIOReturnSuccess;
+ io_iterator_t hidObjectIterator = 0;
+ io_object_t hidDevice = 0;
+
+ // Set up a matching dictionary to search the I/O Registry by class
+ // name for all HID class devices
+ hidMatchDictionary = IOServiceMatching([self remoteControlDeviceName]);
+
+ // Now search I/O Registry for matching devices.
+ ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, hidMatchDictionary, &hidObjectIterator);
+
+ if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0)) {
+ hidDevice = IOIteratorNext(hidObjectIterator);
+ }
+
+ // release the iterator
+ IOObjectRelease(hidObjectIterator);
+
+ return hidDevice;
+}
+
+@end
+
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/IRKeyboardEmu.h b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/IRKeyboardEmu.h
new file mode 100644
index 0000000000..8a842da8f3
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/IRKeyboardEmu.h
@@ -0,0 +1,16 @@
+//
+// IRKeyboardEmu.h
+// XBMCHelper
+//
+// Created by Stephan Diederich on 14.04.09.
+// Copyright 2009 __MyCompanyName__. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "AppleRemote.h"
+
+@interface IRKeyboardEmu : AppleRemote {
+
+}
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/IRKeyboardEmu.m b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/IRKeyboardEmu.m
new file mode 100644
index 0000000000..5350e16c0c
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/IRKeyboardEmu.m
@@ -0,0 +1,26 @@
+//
+// IRKeyboardEmu.m
+// XBMCHelper
+//
+// Created by Stephan Diederich on 14.04.09.
+// Copyright 2009 __MyCompanyName__. All rights reserved.
+//
+
+#import "IRKeyboardEmu.h"
+
+
+@implementation IRKeyboardEmu
+
++ (const char*) remoteControlDeviceName {
+ return "IRKeyboardEmu";
+}
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown {
+ if (pressedDown == NO && event == kRemoteButtonMenu_Hold) {
+ // There is no seperate event for pressed down on menu hold. We are simulating that event here
+ [super sendRemoteButtonEvent:event pressedDown:YES];
+ }
+
+ [super sendRemoteButtonEvent:event pressedDown:pressedDown];
+}
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/KeyspanFrontRowControl.h b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/KeyspanFrontRowControl.h
new file mode 100644
index 0000000000..3a8224154f
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/KeyspanFrontRowControl.h
@@ -0,0 +1,39 @@
+/*****************************************************************************
+ * KeyspanFrontRowControl.h
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+
+#import <Cocoa/Cocoa.h>
+#import "HIDRemoteControlDevice.h"
+
+/* Interacts with the Keyspan FrontRow Remote Control HID device
+ The class is not thread safe
+ */
+@interface KeyspanFrontRowControl : HIDRemoteControlDevice {
+
+}
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/KeyspanFrontRowControl.m b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/KeyspanFrontRowControl.m
new file mode 100644
index 0000000000..7dbcc853ec
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/KeyspanFrontRowControl.m
@@ -0,0 +1,87 @@
+/*****************************************************************************
+ * KeyspanFrontRowControl.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import "KeyspanFrontRowControl.h"
+#import <mach/mach.h>
+#import <mach/mach_error.h>
+#import <IOKit/IOKitLib.h>
+#import <IOKit/IOCFPlugIn.h>
+#import <IOKit/hid/IOHIDKeys.h>
+
+@implementation KeyspanFrontRowControl
+
+- (void) setCookieMappingInDictionary: (NSMutableDictionary*) _cookieToButtonMapping {
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlus] forKey:@"11_18_99_10_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMinus] forKey:@"11_18_98_10_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu] forKey:@"11_18_58_10_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay] forKey:@"11_18_61_10_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight] forKey:@"11_18_96_10_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft] forKey:@"11_18_97_10_"];
+ /* hold events are not being send by this device
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonRight_Hold] forKey:@"14_6_4_2_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonLeft_Hold] forKey:@"14_6_3_2_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonMenu_Hold] forKey:@"14_6_14_6_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteButtonPlay_Sleep] forKey:@"18_14_6_18_14_6_"];
+ [_cookieToButtonMapping setObject:[NSNumber numberWithInt:kRemoteControl_Switched] forKey:@"19_"];
+ */
+}
+
++ (io_object_t) findRemoteDevice {
+ CFMutableDictionaryRef hidMatchDictionary = NULL;
+ IOReturn ioReturnValue = kIOReturnSuccess;
+ io_iterator_t hidObjectIterator = 0;
+ io_object_t hidDevice = 0;
+ SInt32 idVendor = 1741;
+ SInt32 idProduct = 0x420;
+
+ // Set up a matching dictionary to search the I/O Registry by class
+ // name for all HID class devices
+ hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey);
+
+ CFNumberRef numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idVendor);
+ CFDictionaryAddValue(hidMatchDictionary, CFSTR(kIOHIDVendorIDKey), numberRef);
+ CFRelease(numberRef);
+
+ numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &idProduct);
+ CFDictionaryAddValue(hidMatchDictionary, CFSTR(kIOHIDProductIDKey), numberRef);
+ CFRelease(numberRef);
+
+ // Now search I/O Registry for matching devices.
+ ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, hidMatchDictionary, &hidObjectIterator);
+
+ if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0)) {
+ hidDevice = IOIteratorNext(hidObjectIterator);
+ }
+
+ // release the iterator
+ IOObjectRelease(hidObjectIterator);
+
+ return hidDevice;
+
+}
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/MultiClickRemoteBehavior.h b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/MultiClickRemoteBehavior.h
new file mode 100644
index 0000000000..8b288604f7
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/MultiClickRemoteBehavior.h
@@ -0,0 +1,90 @@
+/*****************************************************************************
+ * MultiClickRemoteBehavior.h
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+
+#import <Cocoa/Cocoa.h>
+#import "RemoteControl.h"
+
+/**
+ A behavior that adds multiclick and hold events on top of a device.
+ Events are generated and send to a delegate
+ */
+@interface MultiClickRemoteBehavior : NSObject {
+ id delegate;
+
+ // state for simulating plus/minus hold
+ BOOL simulateHoldEvents;
+ BOOL lastEventSimulatedHold;
+ RemoteControlEventIdentifier lastHoldEvent;
+ NSTimeInterval lastHoldEventTime;
+
+ // state for multi click
+ unsigned int clickCountEnabledButtons;
+ NSTimeInterval maxClickTimeDifference;
+ NSTimeInterval lastClickCountEventTime;
+ RemoteControlEventIdentifier lastClickCountEvent;
+ unsigned int eventClickCount;
+}
+
+- (id) init;
+
+// Delegates are not retained
+- (void) setDelegate: (id) delegate;
+- (id) delegate;
+
+// Simulating hold events does deactivate sending of individual requests for pressed down/released.
+// Instead special hold events are being triggered when the user is pressing and holding a button for a small period.
+// Simulation is activated only for those buttons and remote control that do not have a seperate event already
+- (BOOL) simulateHoldEvent;
+- (void) setSimulateHoldEvent: (BOOL) value;
+
+// click counting makes it possible to recognize if the user has pressed a button repeatedly
+// click counting does delay each event as it has to wait if there is another event (second click)
+// therefore there is a slight time difference (maximumClickCountTimeDifference) between a single click
+// of the user and the call of your delegate method
+// click counting can be enabled individually for specific buttons. Use the property clickCountEnableButtons to
+// set the buttons for which click counting shall be enabled
+- (BOOL) clickCountingEnabled;
+- (void) setClickCountingEnabled: (BOOL) value;
+
+- (unsigned int) clickCountEnabledButtons;
+- (void) setClickCountEnabledButtons: (unsigned int)value;
+
+// the maximum time difference till which clicks are recognized as multi clicks
+- (NSTimeInterval) maximumClickCountTimeDifference;
+- (void) setMaximumClickCountTimeDifference: (NSTimeInterval) timeDiff;
+
+@end
+
+/*
+ * Method definitions for the delegate of the MultiClickRemoteBehavior class
+ */
+@interface NSObject(MultiClickRemoteBehaviorDelegate)
+
+- (void) remoteButton: (RemoteControlEventIdentifier)buttonIdentifier pressedDown: (BOOL) pressedDown clickCount: (unsigned int) count;
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/MultiClickRemoteBehavior.m b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/MultiClickRemoteBehavior.m
new file mode 100644
index 0000000000..4b40fb3df8
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/MultiClickRemoteBehavior.m
@@ -0,0 +1,210 @@
+/*****************************************************************************
+ * MultiClickRemoteBehavior.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import "MultiClickRemoteBehavior.h"
+
+const NSTimeInterval DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE=0.35;
+const NSTimeInterval HOLD_RECOGNITION_TIME_INTERVAL=0.4;
+
+@implementation MultiClickRemoteBehavior
+
+- (id) init {
+ if (self = [super init]) {
+ maxClickTimeDifference = DEFAULT_MAXIMUM_CLICK_TIME_DIFFERENCE;
+ }
+ return self;
+}
+
+// Delegates are not retained!
+// http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/chapter_6_section_4.html
+// Delegating objects do not (and should not) retain their delegates.
+// However, clients of delegating objects (applications, usually) are responsible for ensuring that their delegates are around
+// to receive delegation messages. To do this, they may have to retain the delegate.
+- (void) setDelegate: (id) _delegate {
+ if (_delegate && [_delegate respondsToSelector:@selector(remoteButton:pressedDown:clickCount:)]==NO) return;
+
+ delegate = _delegate;
+}
+- (id) delegate {
+ return delegate;
+}
+
+- (BOOL) simulateHoldEvent {
+ return simulateHoldEvents;
+}
+- (void) setSimulateHoldEvent: (BOOL) value {
+ simulateHoldEvents = value;
+}
+
+- (BOOL) simulatesHoldForButtonIdentifier: (RemoteControlEventIdentifier) identifier remoteControl: (RemoteControl*) remoteControl {
+ // we do that check only for the normal button identifiers as we would check for hold support for hold events instead
+ if (identifier > (1 << EVENT_TO_HOLD_EVENT_OFFSET)) return NO;
+
+ return [self simulateHoldEvent] && [remoteControl sendsEventForButtonIdentifier: (identifier << EVENT_TO_HOLD_EVENT_OFFSET)]==NO;
+}
+
+- (BOOL) clickCountingEnabled {
+ return clickCountEnabledButtons != 0;
+}
+- (void) setClickCountingEnabled: (BOOL) value {
+ if (value) {
+ [self setClickCountEnabledButtons: kRemoteButtonPlus | kRemoteButtonMinus | kRemoteButtonPlay | kRemoteButtonLeft | kRemoteButtonRight | kRemoteButtonMenu];
+ } else {
+ [self setClickCountEnabledButtons: 0];
+ }
+}
+
+- (unsigned int) clickCountEnabledButtons {
+ return clickCountEnabledButtons;
+}
+- (void) setClickCountEnabledButtons: (unsigned int)value {
+ clickCountEnabledButtons = value;
+}
+
+- (NSTimeInterval) maximumClickCountTimeDifference {
+ return maxClickTimeDifference;
+}
+- (void) setMaximumClickCountTimeDifference: (NSTimeInterval) timeDiff {
+ maxClickTimeDifference = timeDiff;
+}
+
+- (void) sendSimulatedHoldEvent: (id) time {
+ BOOL startSimulateHold = NO;
+ RemoteControlEventIdentifier event = lastHoldEvent;
+ @synchronized(self) {
+ startSimulateHold = (lastHoldEvent>0 && lastHoldEventTime == [time doubleValue]);
+ }
+ if (startSimulateHold) {
+ lastEventSimulatedHold = YES;
+ event = (event << EVENT_TO_HOLD_EVENT_OFFSET);
+ [delegate remoteButton:event pressedDown: YES clickCount: 1];
+ }
+}
+
+- (void) executeClickCountEvent: (NSArray*) values {
+ RemoteControlEventIdentifier event = [[values objectAtIndex: 0] unsignedIntValue];
+ NSTimeInterval eventTimePoint = [[values objectAtIndex: 1] doubleValue];
+
+ BOOL finishedClicking = NO;
+ int finalClickCount = eventClickCount;
+
+ @synchronized(self) {
+ finishedClicking = (event != lastClickCountEvent || eventTimePoint == lastClickCountEventTime);
+ if (finishedClicking) {
+ eventClickCount = 0;
+ lastClickCountEvent = 0;
+ lastClickCountEventTime = 0;
+ }
+ }
+
+ if (finishedClicking) {
+ [delegate remoteButton:event pressedDown: YES clickCount:finalClickCount];
+ // trigger a button release event, too
+ [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow:0.1]];
+ [delegate remoteButton:event pressedDown: NO clickCount:finalClickCount];
+ }
+}
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown remoteControl: (RemoteControl*) remoteControl {
+ if (!delegate) return;
+
+ BOOL clickCountingForEvent = ([self clickCountEnabledButtons] & event) == event;
+
+ if ([self simulatesHoldForButtonIdentifier: event remoteControl: remoteControl] && lastClickCountEvent==0) {
+ if (pressedDown) {
+ // wait to see if it is a hold
+ lastHoldEvent = event;
+ lastHoldEventTime = [NSDate timeIntervalSinceReferenceDate];
+ [self performSelector:@selector(sendSimulatedHoldEvent:)
+ withObject:[NSNumber numberWithDouble:lastHoldEventTime]
+ afterDelay:HOLD_RECOGNITION_TIME_INTERVAL];
+ return;
+ } else {
+ if (lastEventSimulatedHold) {
+ // it was a hold
+ // send an event for "hold release"
+ event = (event << EVENT_TO_HOLD_EVENT_OFFSET);
+ lastHoldEvent = 0;
+ lastEventSimulatedHold = NO;
+
+ [delegate remoteButton:event pressedDown: pressedDown clickCount:1];
+ return;
+ } else {
+ RemoteControlEventIdentifier previousEvent = lastHoldEvent;
+ @synchronized(self) {
+ lastHoldEvent = 0;
+ }
+
+ // in case click counting is enabled we have to setup the state for that, too
+ if (clickCountingForEvent) {
+ lastClickCountEvent = previousEvent;
+ lastClickCountEventTime = lastHoldEventTime;
+ NSNumber* eventNumber;
+ NSNumber* timeNumber;
+ eventClickCount = 1;
+ timeNumber = [NSNumber numberWithDouble:lastClickCountEventTime];
+ eventNumber= [NSNumber numberWithUnsignedInt:previousEvent];
+ NSTimeInterval diffTime = maxClickTimeDifference-([NSDate timeIntervalSinceReferenceDate]-lastHoldEventTime);
+ [self performSelector: @selector(executeClickCountEvent:)
+ withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
+ afterDelay: diffTime];
+ // we do not return here because we are still in the press-release event
+ // that will be consumed below
+ } else {
+ // trigger the pressed down event that we consumed first
+ [delegate remoteButton:event pressedDown: YES clickCount:1];
+ }
+ }
+ }
+ }
+
+ if (clickCountingForEvent) {
+ if (pressedDown == NO) return;
+
+ NSNumber* eventNumber;
+ NSNumber* timeNumber;
+ @synchronized(self) {
+ lastClickCountEventTime = [NSDate timeIntervalSinceReferenceDate];
+ if (lastClickCountEvent == event) {
+ eventClickCount = eventClickCount + 1;
+ } else {
+ eventClickCount = 1;
+ }
+ lastClickCountEvent = event;
+ timeNumber = [NSNumber numberWithDouble:lastClickCountEventTime];
+ eventNumber= [NSNumber numberWithUnsignedInt:event];
+ }
+ [self performSelector: @selector(executeClickCountEvent:)
+ withObject: [NSArray arrayWithObjects:eventNumber, timeNumber, nil]
+ afterDelay: maxClickTimeDifference];
+ } else {
+ [delegate remoteButton:event pressedDown: pressedDown clickCount:1];
+ }
+
+}
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControl.h b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControl.h
new file mode 100644
index 0000000000..34e9d5e19d
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControl.h
@@ -0,0 +1,102 @@
+/*****************************************************************************
+ * RemoteControl.h
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+
+// notifaction names that are being used to signal that an application wants to
+// have access to the remote control device or if the application has finished
+// using the remote control device
+extern NSString* REQUEST_FOR_REMOTE_CONTROL_NOTIFCATION;
+extern NSString* FINISHED_USING_REMOTE_CONTROL_NOTIFICATION;
+
+// keys used in user objects for distributed notifications
+extern NSString* kRemoteControlDeviceName;
+extern NSString* kApplicationIdentifier;
+extern NSString* kTargetApplicationIdentifier;
+
+// we have a 6 bit offset to make a hold event out of a normal event
+#define EVENT_TO_HOLD_EVENT_OFFSET 6
+
+@class RemoteControl;
+
+typedef enum _RemoteControlEventIdentifier {
+ // normal events
+ kRemoteButtonPlus =1<<1,
+ kRemoteButtonMinus =1<<2,
+ kRemoteButtonMenu =1<<3,
+ kRemoteButtonPlay =1<<4,
+ kRemoteButtonRight =1<<5,
+ kRemoteButtonLeft =1<<6,
+
+ // hold events
+ kRemoteButtonPlus_Hold =1<<7,
+ kRemoteButtonMinus_Hold =1<<8,
+ kRemoteButtonMenu_Hold =1<<9,
+ kRemoteButtonPlay_Hold =1<<10,
+ kRemoteButtonRight_Hold =1<<11,
+ kRemoteButtonLeft_Hold =1<<12,
+
+ // special events (not supported by all devices)
+ kRemoteControl_Switched =1<<13,
+} RemoteControlEventIdentifier;
+
+@interface NSObject(RemoteControlDelegate)
+
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown remoteControl: (RemoteControl*) remoteControl;
+
+@end
+
+/*
+ Base Interface for Remote Control devices
+ */
+@interface RemoteControl : NSObject {
+ id delegate;
+}
+
+// returns nil if the remote control device is not available
+- (id) initWithDelegate: (id) remoteControlDelegate;
+
+- (void) setListeningToRemote: (BOOL) value;
+- (BOOL) isListeningToRemote;
+
+- (BOOL) isOpenInExclusiveMode;
+- (void) setOpenInExclusiveMode: (BOOL) value;
+
+- (IBAction) startListening: (id) sender;
+- (IBAction) stopListening: (id) sender;
+
+// is this remote control sending the given event?
+- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier;
+
+// sending of notifications between applications
++ (void) sendFinishedNotifcationForAppIdentifier: (NSString*) identifier;
++ (void) sendRequestForRemoteControlNotification;
+
+// name of the device
++ (const char*) remoteControlDeviceName;
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControl.m b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControl.m
new file mode 100644
index 0000000000..bf224dd1d8
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControl.m
@@ -0,0 +1,102 @@
+/*****************************************************************************
+ * RemoteControl.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import "RemoteControl.h"
+
+// notifaction names that are being used to signal that an application wants to
+// have access to the remote control device or if the application has finished
+// using the remote control device
+NSString* REQUEST_FOR_REMOTE_CONTROL_NOTIFCATION = @"mac.remotecontrols.RequestForRemoteControl";
+NSString* FINISHED_USING_REMOTE_CONTROL_NOTIFICATION = @"mac.remotecontrols.FinishedUsingRemoteControl";
+
+// keys used in user objects for distributed notifications
+NSString* kRemoteControlDeviceName = @"RemoteControlDeviceName";
+NSString* kApplicationIdentifier = @"CFBundleIdentifier";
+// bundle identifier of the application that should get access to the remote control
+// this key is being used in the FINISHED notification only
+NSString* kTargetApplicationIdentifier = @"TargetBundleIdentifier";
+
+
+@implementation RemoteControl
+
+// returns nil if the remote control device is not available
+- (id) initWithDelegate: (id) _remoteControlDelegate {
+ if (self = [super init]) {
+ delegate = _remoteControlDelegate;
+ }
+ return self;
+}
+
+- (void) dealloc {
+ [super dealloc];
+}
+
+- (void) setListeningToRemote: (BOOL) value {
+}
+- (BOOL) isListeningToRemote {
+ return NO;
+}
+
+- (IBAction) startListening: (id) sender {
+}
+- (IBAction) stopListening: (id) sender {
+
+}
+
+- (BOOL) isOpenInExclusiveMode {
+ return YES;
+}
+- (void) setOpenInExclusiveMode: (BOOL) value {
+}
+
+- (BOOL) sendsEventForButtonIdentifier: (RemoteControlEventIdentifier) identifier {
+ return YES;
+}
+
++ (void) sendDistributedNotification: (NSString*) notificationName targetBundleIdentifier: (NSString*) targetIdentifier {
+ NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys: [NSString stringWithCString:[self remoteControlDeviceName] encoding:NSASCIIStringEncoding],
+ kRemoteControlDeviceName, [[NSBundle mainBundle] bundleIdentifier], kApplicationIdentifier,
+ targetIdentifier, kTargetApplicationIdentifier, nil];
+
+ [[NSDistributedNotificationCenter defaultCenter] postNotificationName:notificationName
+ object:nil
+ userInfo:userInfo
+ deliverImmediately:YES];
+}
+
++ (void) sendFinishedNotifcationForAppIdentifier: (NSString*) identifier {
+ [self sendDistributedNotification:FINISHED_USING_REMOTE_CONTROL_NOTIFICATION targetBundleIdentifier:identifier];
+}
++ (void) sendRequestForRemoteControlNotification {
+ [self sendDistributedNotification:REQUEST_FOR_REMOTE_CONTROL_NOTIFCATION targetBundleIdentifier:nil];
+}
+
++ (const char*) remoteControlDeviceName {
+ return NULL;
+}
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControlContainer.h b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControlContainer.h
new file mode 100644
index 0000000000..e665ae1eab
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControlContainer.h
@@ -0,0 +1,38 @@
+/*****************************************************************************
+ * RemoteControlContainer.h
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import <Cocoa/Cocoa.h>
+#import "RemoteControl.h"
+
+@interface RemoteControlContainer : RemoteControl {
+ NSMutableArray* remoteControls;
+}
+
+- (BOOL) instantiateAndAddRemoteControlDeviceWithClass: (Class) clazz;
+- (unsigned int) count;
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControlContainer.m b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControlContainer.m
new file mode 100644
index 0000000000..8517ea733e
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/RemoteControlWrapper/RemoteControlContainer.m
@@ -0,0 +1,114 @@
+/*****************************************************************************
+ * RemoteControlContainer.m
+ * RemoteControlWrapper
+ *
+ * Created by Martin Kahr on 11.03.06 under a MIT-style license.
+ * Copyright (c) 2006 martinkahr.com. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#import "RemoteControlContainer.h"
+
+
+@implementation RemoteControlContainer
+
+- (id) initWithDelegate: (id) _remoteControlDelegate {
+ if (self = [super initWithDelegate:_remoteControlDelegate]) {
+ remoteControls = [[NSMutableArray alloc] init];
+ }
+ return self;
+}
+
+- (void) dealloc {
+ [self stopListening: self];
+ [remoteControls release];
+ [super dealloc];
+}
+
+- (BOOL) instantiateAndAddRemoteControlDeviceWithClass: (Class) clazz {
+ RemoteControl* remoteControl = [[clazz alloc] initWithDelegate: delegate];
+ if (remoteControl) {
+ [remoteControls addObject: remoteControl];
+ [remoteControl addObserver: self forKeyPath:@"listeningToRemote" options:NSKeyValueObservingOptionNew context:nil];
+ return YES;
+ }
+ return NO;
+}
+
+- (unsigned int) count {
+ return [remoteControls count];
+}
+
+- (void) reset {
+ [self willChangeValueForKey:@"listeningToRemote"];
+ [self didChangeValueForKey:@"listeningToRemote"];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+ [self reset];
+}
+
+- (void) setListeningToRemote: (BOOL) value {
+ int i;
+ for(i=0; i < [remoteControls count]; i++) {
+ [[remoteControls objectAtIndex: i] setListeningToRemote: value];
+ }
+ if (value && value != [self isListeningToRemote]) [self performSelector:@selector(reset) withObject:nil afterDelay:0.01];
+}
+- (BOOL) isListeningToRemote {
+ int i;
+ for(i=0; i < [remoteControls count]; i++) {
+ if ([[remoteControls objectAtIndex: i] isListeningToRemote]) {
+ return YES;
+ }
+ }
+ return NO;
+}
+
+- (IBAction) startListening: (id) sender {
+ int i;
+ for(i=0; i < [remoteControls count]; i++) {
+ [[remoteControls objectAtIndex: i] startListening: sender];
+ }
+}
+- (IBAction) stopListening: (id) sender {
+ int i;
+ for(i=0; i < [remoteControls count]; i++) {
+ [[remoteControls objectAtIndex: i] stopListening: sender];
+ }
+}
+
+- (BOOL) isOpenInExclusiveMode {
+ BOOL mode = YES;
+ int i;
+ for(i=0; i < [remoteControls count]; i++) {
+ mode = mode && ([[remoteControls objectAtIndex: i] isOpenInExclusiveMode]);
+ }
+ return mode;
+}
+- (void) setOpenInExclusiveMode: (BOOL) value {
+ int i;
+ for(i=0; i < [remoteControls count]; i++) {
+ [[remoteControls objectAtIndex: i] setOpenInExclusiveMode:value];
+ }
+}
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/XBMCDebugHelpers.h b/tools/EventClients/Clients/OSXRemote/XBMCDebugHelpers.h
new file mode 100644
index 0000000000..8650f87412
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/XBMCDebugHelpers.h
@@ -0,0 +1,45 @@
+//
+// XBMCDebugHelpers.h
+// xbmclauncher
+//
+// Created by Stephan Diederich on 21.09.08.
+// Copyright 2008 University Heidelberg. All rights reserved.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#import <Cocoa/Cocoa.h>
+
+/*
+ * Stuff below was taken from LoggingUtils.h of ATVFiles
+ *
+ * Just some utility macros for logging...
+ *
+ * Created by Eric Steil III on 4/1/07.
+ // Copyright (C) 2007-2008 Eric Steil III
+ *
+ */
+
+#ifdef DEBUG
+#define LOG(s, ...) NSLog(@"[DEBUG] " s, ##__VA_ARGS__)
+#define ILOG(s, ...) NSLog(@"[INFO] " s, ##__VA_ARGS__)
+#define ELOG(s, ...) NSLog(@"[ERROR] " s, ##__VA_ARGS__)
+#define DLOG(s, ...) LOG(s, ##__VA_ARGS__)
+#else
+#define LOG(s, ...)
+#define ILOG(s, ...) NSLog(@"[INFO] " s, ##__VA_ARGS__)
+#define ELOG(s, ...) NSLog(@"[ERROR] " s, ##__VA_ARGS__)
+#define DLOG(s, ...) LOG(s, ##__VA_ARGS__)
+#endif
+
+#define PRINT_SIGNATURE() LOG(@"%s", __PRETTY_FUNCTION__)
diff --git a/tools/EventClients/Clients/OSXRemote/XBMCHelper.h b/tools/EventClients/Clients/OSXRemote/XBMCHelper.h
new file mode 100644
index 0000000000..9b194a9fd7
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/XBMCHelper.h
@@ -0,0 +1,29 @@
+//
+// XBMCHelper.h
+// xbmchelper
+//
+// Created by Stephan Diederich on 11/12/08.
+// Copyright 2008 University Heidelberg. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+#import "xbmcclientwrapper.h"
+
+@class AppleRemote, MultiClickRemoteBehavior;
+
+@interface XBMCHelper : NSObject {
+ AppleRemote* mp_remote_control;
+ XBMCClientWrapper* mp_wrapper;
+ NSString* mp_app_path;
+ NSString* mp_home_path;
+ bool m_verbose;
+}
+
+- (void) enableVerboseMode:(bool) f_really;
+
+- (void) setApplicationPath:(NSString*) fp_app_path;
+- (void) setApplicationHome:(NSString*) fp_home_path;
+
+- (void) connectToServer:(NSString*) fp_server withMode:(eRemoteMode) f_mode withTimeout:(double) f_timeout;
+- (void) disconnect;
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/XBMCHelper.m b/tools/EventClients/Clients/OSXRemote/XBMCHelper.m
new file mode 100644
index 0000000000..5e0223e11a
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/XBMCHelper.m
@@ -0,0 +1,222 @@
+//
+// XBMCHelper.m
+// xbmchelper
+//
+// Created by Stephan Diederich on 11/12/08.
+// Copyright 2008 University Heidelberg. All rights reserved.
+//
+
+#import "XBMCHelper.h"
+#import "remotecontrolwrapper/AppleRemote.h"
+#import "XBMCDebugHelpers.h"
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+@implementation XBMCHelper
+- (id) init{
+ PRINT_SIGNATURE();
+ if( ![super init] ){
+ return nil;
+ }
+ mp_wrapper = nil;
+ mp_remote_control = [[[AppleRemote alloc] initWithDelegate: self] retain];
+ [mp_remote_control setProcessesBacklog:true];
+ [mp_remote_control setOpenInExclusiveMode:true];
+ if( ! mp_remote_control ){
+ NSException* myException = [NSException
+ exceptionWithName:@"AppleRemoteInitExecption"
+ reason:@"AppleRemote could not be initialized"
+ userInfo:nil];
+ @throw myException;
+ }
+ [mp_remote_control startListening: self];
+ if(![mp_remote_control isListeningToRemote]){
+ ELOG(@"Warning: XBMCHelper could not open the IR-Device in exclusive mode. Other remote control apps running?");
+ }
+ return self;
+}
+
+- (void) dealloc{
+ PRINT_SIGNATURE();
+ [mp_remote_control stopListening: self];
+ [mp_remote_control release];
+ [mp_wrapper release];
+ [mp_app_path release];
+ [mp_home_path release];
+ [super dealloc];
+}
+
+//----------------------------------------------------------------------------
+- (void) checkAndLaunchApp
+{
+ if(!mp_app_path || ![mp_app_path length]){
+ ELOG(@"No executable set. Nothing to launch");
+ return;
+ }
+
+ NSFileManager *fileManager = [NSFileManager defaultManager];
+ if(![fileManager fileExistsAtPath:mp_app_path]){
+ ELOG(@"Path does not exist: %@. Cannot launch executable", mp_app_path);
+ return;
+ }
+ if(mp_home_path && [mp_home_path length])
+ setenv("XBMC_HOME", [mp_home_path cString], 1);
+ //launch or activate xbmc
+ if(![[NSWorkspace sharedWorkspace] launchApplication:mp_app_path]){
+ ELOG(@"Error launching %@", mp_app_path);
+ }
+}
+
+//----------------------------------------------------------------------------
+- (void) sendRemoteButtonEvent: (RemoteControlEventIdentifier) event pressedDown: (BOOL) pressedDown remoteControl: (RemoteControl*) remoteControl;
+{
+ if(m_verbose) {
+ //do some logging here
+ //[self logButton: event press;
+ }
+
+ switch(event){
+ case kRemoteButtonPlay:
+ if(pressedDown) [mp_wrapper handleEvent:ATV_BUTTON_PLAY];
+ break;
+ case kRemoteButtonPlay_Hold:
+ if(pressedDown) [mp_wrapper handleEvent:ATV_BUTTON_PLAY_H];
+ break;
+ case kRemoteButtonRight:
+ if(pressedDown)
+ [mp_wrapper handleEvent:ATV_BUTTON_RIGHT];
+ else
+ [mp_wrapper handleEvent:ATV_BUTTON_RIGHT_RELEASE];
+ break;
+ case kRemoteButtonRight_Hold:
+ if(pressedDown)
+ [mp_wrapper handleEvent:ATV_BUTTON_RIGHT_H];
+ else
+ [mp_wrapper handleEvent:ATV_BUTTON_RIGHT_H_RELEASE];
+ break;
+ case kRemoteButtonLeft:
+ if(pressedDown)
+ [mp_wrapper handleEvent:ATV_BUTTON_LEFT];
+ else
+ [mp_wrapper handleEvent:ATV_BUTTON_LEFT_RELEASE];
+ break;
+ case kRemoteButtonLeft_Hold:
+ if(pressedDown)
+ [mp_wrapper handleEvent:ATV_BUTTON_LEFT_H];
+ else
+ [mp_wrapper handleEvent:ATV_BUTTON_LEFT_H_RELEASE];
+ break;
+ case kRemoteButtonPlus:
+ if(pressedDown)
+ [mp_wrapper handleEvent:ATV_BUTTON_UP];
+ else
+ [mp_wrapper handleEvent:ATV_BUTTON_UP_RELEASE];
+ break;
+ case kRemoteButtonMinus:
+ if(pressedDown)
+ [mp_wrapper handleEvent:ATV_BUTTON_DOWN];
+ else
+ [mp_wrapper handleEvent:ATV_BUTTON_DOWN_RELEASE];
+ break;
+ case kRemoteButtonMenu:
+ if(pressedDown){
+ [self checkAndLaunchApp]; //launch mp_app_path if it's not running
+ [mp_wrapper handleEvent:ATV_BUTTON_MENU];
+ }
+ break;
+ case kRemoteButtonMenu_Hold:
+ if(pressedDown) [mp_wrapper handleEvent:ATV_BUTTON_MENU_H];
+ break;
+ case kRemoteControl_Switched:
+ if(pressedDown) [mp_wrapper switchRemote: [mp_remote_control deviceID]];
+ break;
+ default:
+ NSLog(@"Oha, remote button not recognized %i pressed/released %i", event, pressedDown);
+ }
+}
+
+//----------------------------------------------------------------------------
+- (void) connectToServer:(NSString*) fp_server withMode:(eRemoteMode) f_mode withTimeout:(double) f_timeout{
+ if(mp_wrapper)
+ [self disconnect];
+ mp_wrapper = [[XBMCClientWrapper alloc] initWithMode:f_mode serverAddress:fp_server verbose:m_verbose];
+ [mp_wrapper setUniversalModeTimeout:f_timeout];
+}
+
+//----------------------------------------------------------------------------
+- (void) disconnect{
+ [mp_wrapper release];
+ mp_wrapper = nil;
+}
+
+//----------------------------------------------------------------------------
+- (void) enableVerboseMode:(bool) f_really{
+ m_verbose = f_really;
+ [mp_wrapper enableVerboseMode:f_really];
+}
+
+//----------------------------------------------------------------------------
+- (void) setApplicationPath:(NSString*) fp_app_path{
+ if (mp_app_path != fp_app_path) {
+ [mp_app_path release];
+ mp_app_path = [[fp_app_path stringByStandardizingPath] retain];
+ }
+}
+
+//----------------------------------------------------------------------------
+- (void) setApplicationHome:(NSString*) fp_home_path{
+ if (mp_home_path != fp_home_path) {
+ [mp_home_path release];
+ mp_home_path = [[fp_home_path stringByStandardizingPath] retain];
+ }
+}
+// NSString* pressed;
+// NSString* buttonName;
+// if (pressedDown) pressed = @"(pressed)"; else pressed = @"(released)";
+//
+// switch(event) {
+// case kRemoteButtonPlus:
+// buttonName = @"Volume up";
+// break;
+// case kRemoteButtonMinus:
+// buttonName = @"Volume down";
+// break;
+// case kRemoteButtonMenu:
+// buttonName = @"Menu";
+// break;
+// case kRemoteButtonPlay:
+// buttonName = @"Play";
+// break;
+// case kRemoteButtonRight:
+// buttonName = @"Right";
+// break;
+// case kRemoteButtonLeft:
+// buttonName = @"Left";
+// break;
+// case kRemoteButtonRight_Hold:
+// buttonName = @"Right holding";
+// break;
+// case kRemoteButtonLeft_Hold:
+// buttonName = @"Left holding";
+// break;
+// case kRemoteButtonPlus_Hold:
+// buttonName = @"Volume up holding";
+// break;
+// case kRemoteButtonMinus_Hold:
+// buttonName = @"Volume down holding";
+// break;
+// case kRemoteButtonPlay_Hold:
+// buttonName = @"Play (sleep mode)";
+// break;
+// case kRemoteButtonMenu_Hold:
+// buttonName = @"Menu holding";
+// break;
+// case kRemoteControl_Switched:
+// buttonName = @"Remote Control Switched";
+// break;
+// default:
+// break;
+// }
+// NSLog(@"%@ %@", pressed, buttonName);
+// }
+
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/XBMCHelper.xcodeproj/project.pbxproj b/tools/EventClients/Clients/OSXRemote/XBMCHelper.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..0c6c5ef7c9
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/XBMCHelper.xcodeproj/project.pbxproj
@@ -0,0 +1,305 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 45;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ E45F2B1C0F95191E007BCC0B /* IRKeyboardEmu.m in Sources */ = {isa = PBXBuildFile; fileRef = E45F2B1B0F95191E007BCC0B /* IRKeyboardEmu.m */; };
+ E4E62F370F83DB760066AF9D /* xbmchelper_main.mm in Sources */ = {isa = PBXBuildFile; fileRef = E4E62F340F83DB760066AF9D /* xbmchelper_main.mm */; };
+ E4E62F380F83DB760066AF9D /* XBMCHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = E4E62F360F83DB760066AF9D /* XBMCHelper.m */; };
+ E4E62F4A0F83DB830066AF9D /* AppleRemote.m in Sources */ = {isa = PBXBuildFile; fileRef = E4E62F3D0F83DB830066AF9D /* AppleRemote.m */; };
+ E4E62F4B0F83DB830066AF9D /* GlobalKeyboardDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = E4E62F3F0F83DB830066AF9D /* GlobalKeyboardDevice.m */; };
+ E4E62F4C0F83DB830066AF9D /* HIDRemoteControlDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = E4E62F410F83DB830066AF9D /* HIDRemoteControlDevice.m */; };
+ E4E62F4D0F83DB830066AF9D /* KeyspanFrontRowControl.m in Sources */ = {isa = PBXBuildFile; fileRef = E4E62F430F83DB830066AF9D /* KeyspanFrontRowControl.m */; };
+ E4E62F4E0F83DB830066AF9D /* MultiClickRemoteBehavior.m in Sources */ = {isa = PBXBuildFile; fileRef = E4E62F450F83DB830066AF9D /* MultiClickRemoteBehavior.m */; };
+ E4E62F4F0F83DB830066AF9D /* RemoteControl.m in Sources */ = {isa = PBXBuildFile; fileRef = E4E62F470F83DB830066AF9D /* RemoteControl.m */; };
+ E4E62F500F83DB830066AF9D /* RemoteControlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = E4E62F490F83DB830066AF9D /* RemoteControlContainer.m */; };
+ E4E62F600F83FB8C0066AF9D /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4E62F5F0F83FB8C0066AF9D /* IOKit.framework */; };
+ E4E62F690F83FBB40066AF9D /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4E62F680F83FBB40066AF9D /* Carbon.framework */; };
+ E4E62FD40F83FD7C0066AF9D /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4E62FD30F83FD7C0066AF9D /* Cocoa.framework */; };
+ E4E62FE80F83FDD90066AF9D /* xbmcclientwrapper.mm in Sources */ = {isa = PBXBuildFile; fileRef = E4E62FE60F83FDD90066AF9D /* xbmcclientwrapper.mm */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 8DD76F7B0486A8DE00D96B5E /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; };
+ 8DD76F7E0486A8DE00D96B5E /* XBMCHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = XBMCHelper; sourceTree = BUILT_PRODUCTS_DIR; };
+ E45F2B1A0F95191E007BCC0B /* IRKeyboardEmu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IRKeyboardEmu.h; sourceTree = "<group>"; };
+ E45F2B1B0F95191E007BCC0B /* IRKeyboardEmu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IRKeyboardEmu.m; sourceTree = "<group>"; };
+ E4E62F340F83DB760066AF9D /* xbmchelper_main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = xbmchelper_main.mm; sourceTree = "<group>"; };
+ E4E62F350F83DB760066AF9D /* XBMCHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMCHelper.h; sourceTree = "<group>"; };
+ E4E62F360F83DB760066AF9D /* XBMCHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XBMCHelper.m; sourceTree = "<group>"; };
+ E4E62F3C0F83DB830066AF9D /* AppleRemote.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleRemote.h; sourceTree = "<group>"; };
+ E4E62F3D0F83DB830066AF9D /* AppleRemote.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppleRemote.m; sourceTree = "<group>"; };
+ E4E62F3E0F83DB830066AF9D /* GlobalKeyboardDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GlobalKeyboardDevice.h; sourceTree = "<group>"; };
+ E4E62F3F0F83DB830066AF9D /* GlobalKeyboardDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GlobalKeyboardDevice.m; sourceTree = "<group>"; };
+ E4E62F400F83DB830066AF9D /* HIDRemoteControlDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HIDRemoteControlDevice.h; sourceTree = "<group>"; };
+ E4E62F410F83DB830066AF9D /* HIDRemoteControlDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HIDRemoteControlDevice.m; sourceTree = "<group>"; };
+ E4E62F420F83DB830066AF9D /* KeyspanFrontRowControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeyspanFrontRowControl.h; sourceTree = "<group>"; };
+ E4E62F430F83DB830066AF9D /* KeyspanFrontRowControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KeyspanFrontRowControl.m; sourceTree = "<group>"; };
+ E4E62F440F83DB830066AF9D /* MultiClickRemoteBehavior.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiClickRemoteBehavior.h; sourceTree = "<group>"; };
+ E4E62F450F83DB830066AF9D /* MultiClickRemoteBehavior.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultiClickRemoteBehavior.m; sourceTree = "<group>"; };
+ E4E62F460F83DB830066AF9D /* RemoteControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteControl.h; sourceTree = "<group>"; };
+ E4E62F470F83DB830066AF9D /* RemoteControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RemoteControl.m; sourceTree = "<group>"; };
+ E4E62F480F83DB830066AF9D /* RemoteControlContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RemoteControlContainer.h; sourceTree = "<group>"; };
+ E4E62F490F83DB830066AF9D /* RemoteControlContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RemoteControlContainer.m; sourceTree = "<group>"; };
+ E4E62F5F0F83FB8C0066AF9D /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
+ E4E62F680F83FBB40066AF9D /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
+ E4E62FD30F83FD7C0066AF9D /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
+ E4E62FE50F83FDD90066AF9D /* xbmcclientwrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xbmcclientwrapper.h; sourceTree = "<group>"; };
+ E4E62FE60F83FDD90066AF9D /* xbmcclientwrapper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = xbmcclientwrapper.mm; sourceTree = "<group>"; };
+ E4E62FE70F83FDD90066AF9D /* XBMCDebugHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XBMCDebugHelpers.h; sourceTree = "<group>"; };
+ E4E630030F8406900066AF9D /* xbmcclient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xbmcclient.h; path = "../../lib/c++/xbmcclient.h"; sourceTree = SOURCE_ROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8DD76F780486A8DE00D96B5E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ E4E62F600F83FB8C0066AF9D /* IOKit.framework in Frameworks */,
+ E4E62F690F83FBB40066AF9D /* Carbon.framework in Frameworks */,
+ E4E62FD40F83FD7C0066AF9D /* Cocoa.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 08FB7794FE84155DC02AAC07 /* XBMCHelper */ = {
+ isa = PBXGroup;
+ children = (
+ 08FB7795FE84155DC02AAC07 /* Source */,
+ C6859E96029091FE04C91782 /* Documentation */,
+ 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
+ 19C28FBDFE9D53C911CA2CBB /* Products */,
+ E4E62F5F0F83FB8C0066AF9D /* IOKit.framework */,
+ E4E62F680F83FBB40066AF9D /* Carbon.framework */,
+ E4E62FD30F83FD7C0066AF9D /* Cocoa.framework */,
+ );
+ name = XBMCHelper;
+ sourceTree = "<group>";
+ };
+ 08FB7795FE84155DC02AAC07 /* Source */ = {
+ isa = PBXGroup;
+ children = (
+ E4E62F3B0F83DB830066AF9D /* RemoteControlWrapper */,
+ E4E630030F8406900066AF9D /* xbmcclient.h */,
+ E4E62F340F83DB760066AF9D /* xbmchelper_main.mm */,
+ E4E62F350F83DB760066AF9D /* XBMCHelper.h */,
+ E4E62F360F83DB760066AF9D /* XBMCHelper.m */,
+ E4E62FE70F83FDD90066AF9D /* XBMCDebugHelpers.h */,
+ E4E62FE50F83FDD90066AF9D /* xbmcclientwrapper.h */,
+ E4E62FE60F83FDD90066AF9D /* xbmcclientwrapper.mm */,
+ );
+ name = Source;
+ sourceTree = "<group>";
+ };
+ 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = {
+ isa = PBXGroup;
+ children = (
+ 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */,
+ );
+ name = "External Frameworks and Libraries";
+ sourceTree = "<group>";
+ };
+ 19C28FBDFE9D53C911CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8DD76F7E0486A8DE00D96B5E /* XBMCHelper */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ C6859E96029091FE04C91782 /* Documentation */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Documentation;
+ sourceTree = "<group>";
+ };
+ E4E62F3B0F83DB830066AF9D /* RemoteControlWrapper */ = {
+ isa = PBXGroup;
+ children = (
+ E4E62F3C0F83DB830066AF9D /* AppleRemote.h */,
+ E4E62F3D0F83DB830066AF9D /* AppleRemote.m */,
+ E4E62F3E0F83DB830066AF9D /* GlobalKeyboardDevice.h */,
+ E4E62F3F0F83DB830066AF9D /* GlobalKeyboardDevice.m */,
+ E4E62F400F83DB830066AF9D /* HIDRemoteControlDevice.h */,
+ E4E62F410F83DB830066AF9D /* HIDRemoteControlDevice.m */,
+ E4E62F420F83DB830066AF9D /* KeyspanFrontRowControl.h */,
+ E4E62F430F83DB830066AF9D /* KeyspanFrontRowControl.m */,
+ E4E62F440F83DB830066AF9D /* MultiClickRemoteBehavior.h */,
+ E4E62F450F83DB830066AF9D /* MultiClickRemoteBehavior.m */,
+ E4E62F460F83DB830066AF9D /* RemoteControl.h */,
+ E4E62F470F83DB830066AF9D /* RemoteControl.m */,
+ E4E62F480F83DB830066AF9D /* RemoteControlContainer.h */,
+ E4E62F490F83DB830066AF9D /* RemoteControlContainer.m */,
+ E45F2B1A0F95191E007BCC0B /* IRKeyboardEmu.h */,
+ E45F2B1B0F95191E007BCC0B /* IRKeyboardEmu.m */,
+ );
+ path = RemoteControlWrapper;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 8DD76F740486A8DE00D96B5E /* XBMCHelper */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1DEB924708733DCA0010E9CD /* Build configuration list for PBXNativeTarget "XBMCHelper" */;
+ buildPhases = (
+ 8DD76F760486A8DE00D96B5E /* Sources */,
+ 8DD76F780486A8DE00D96B5E /* Frameworks */,
+ 8DD76F7B0486A8DE00D96B5E /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = XBMCHelper;
+ productInstallPath = "$(HOME)/bin";
+ productName = XBMCHelper;
+ productReference = 8DD76F7E0486A8DE00D96B5E /* XBMCHelper */;
+ productType = "com.apple.product-type.tool";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 08FB7793FE84155DC02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = 1DEB924B08733DCA0010E9CD /* Build configuration list for PBXProject "XBMCHelper" */;
+ compatibilityVersion = "Xcode 3.1";
+ hasScannedForEncodings = 1;
+ mainGroup = 08FB7794FE84155DC02AAC07 /* XBMCHelper */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8DD76F740486A8DE00D96B5E /* XBMCHelper */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8DD76F760486A8DE00D96B5E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ E4E62F370F83DB760066AF9D /* xbmchelper_main.mm in Sources */,
+ E4E62F380F83DB760066AF9D /* XBMCHelper.m in Sources */,
+ E4E62F4A0F83DB830066AF9D /* AppleRemote.m in Sources */,
+ E4E62F4B0F83DB830066AF9D /* GlobalKeyboardDevice.m in Sources */,
+ E4E62F4C0F83DB830066AF9D /* HIDRemoteControlDevice.m in Sources */,
+ E4E62F4D0F83DB830066AF9D /* KeyspanFrontRowControl.m in Sources */,
+ E4E62F4E0F83DB830066AF9D /* MultiClickRemoteBehavior.m in Sources */,
+ E4E62F4F0F83DB830066AF9D /* RemoteControl.m in Sources */,
+ E4E62F500F83DB830066AF9D /* RemoteControlContainer.m in Sources */,
+ E4E62FE80F83FDD90066AF9D /* xbmcclientwrapper.mm in Sources */,
+ E45F2B1C0F95191E007BCC0B /* IRKeyboardEmu.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 1DEB924808733DCA0010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CONFIGURATION_BUILD_DIR = ../../../osx;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
+ INSTALL_PATH = /usr/local/bin;
+ ONLY_ACTIVE_ARCH = NO;
+ PRODUCT_NAME = XBMCHelper;
+ SDKROOT = macosx10.4;
+ STRIP_STYLE = debugging;
+ };
+ name = Debug;
+ };
+ 1DEB924908733DCA0010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CONFIGURATION_BUILD_DIR = ../../../osx;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_MODEL_TUNING = G5;
+ GCC_VERSION = 4.0;
+ INSTALL_PATH = /usr/local/bin;
+ ONLY_ACTIVE_ARCH = NO;
+ PRODUCT_NAME = XBMCHelper;
+ SDKROOT = macosx10.4;
+ STRIP_STYLE = debugging;
+ };
+ name = Release;
+ };
+ 1DEB924C08733DCA0010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_VERSION = 4.0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PREBINDING = NO;
+ SDKROOT = macosx10.4;
+ };
+ name = Debug;
+ };
+ 1DEB924D08733DCA0010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ GCC_C_LANGUAGE_STANDARD = c99;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ SDKROOT = macosx10.4;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1DEB924708733DCA0010E9CD /* Build configuration list for PBXNativeTarget "XBMCHelper" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB924808733DCA0010E9CD /* Debug */,
+ 1DEB924908733DCA0010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 1DEB924B08733DCA0010E9CD /* Build configuration list for PBXProject "XBMCHelper" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB924C08733DCA0010E9CD /* Debug */,
+ 1DEB924D08733DCA0010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}
diff --git a/tools/EventClients/Clients/OSXRemote/xbmcclientwrapper.h b/tools/EventClients/Clients/OSXRemote/xbmcclientwrapper.h
new file mode 100644
index 0000000000..8664b2d985
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/xbmcclientwrapper.h
@@ -0,0 +1,73 @@
+/*
+ * xbmcclient.cpp
+ * xbmclauncher
+ *
+ * Created by Stephan Diederich on 17.09.08.
+ * Copyright 2008 Stephan Diederich. All rights reserved.
+ *
+ */
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+#import <Cocoa/Cocoa.h>
+
+typedef enum{
+ ATV_BUTTON_DONT_USE_THIS = 0, //don't use zero, as those enums get converted to strings later
+ ATV_BUTTON_PLAY=1,
+ ATV_BUTTON_PLAY_H, //present on ATV>=2.2
+ ATV_BUTTON_RIGHT,
+ ATV_BUTTON_RIGHT_RELEASE,
+ ATV_BUTTON_RIGHT_H, //present on ATV<=2.1 and OSX v?
+ ATV_BUTTON_RIGHT_H_RELEASE,
+ ATV_BUTTON_LEFT,
+ ATV_BUTTON_LEFT_RELEASE,
+ ATV_BUTTON_LEFT_H, //present on ATV<=2.1 and OSX v?
+ ATV_BUTTON_LEFT_H_RELEASE,
+ ATV_BUTTON_UP,
+ ATV_BUTTON_UP_RELEASE,
+ ATV_BUTTON_DOWN,
+ ATV_BUTTON_DOWN_RELEASE,
+ ATV_BUTTON_MENU,
+ ATV_BUTTON_MENU_H,
+ ATV_LEARNED_PLAY,
+ ATV_LEARNED_PAUSE,
+ ATV_LEARNED_STOP,
+ ATV_LEARNED_PREVIOUS,
+ ATV_LEARNED_NEXT,
+ ATV_LEARNED_REWIND, //>=ATV 2.3
+ ATV_LEARNED_REWIND_RELEASE, //>=ATV 2.3
+ ATV_LEARNED_FORWARD, //>=ATV 2.3
+ ATV_LEARNED_FORWARD_RELEASE, //>=ATV 2.3
+ ATV_LEARNED_RETURN,
+ ATV_LEARNED_ENTER,
+ ATV_INVALID_BUTTON
+} eATVClientEvent;
+
+
+typedef enum {
+ DEFAULT_MODE,
+ UNIVERSAL_MODE,
+ MULTIREMOTE_MODE
+} eRemoteMode;
+
+
+@interface XBMCClientWrapper : NSObject{
+ struct XBMCClientWrapperImpl* mp_impl;
+}
+- (id) initWithMode:(eRemoteMode) f_mode serverAddress:(NSString*) fp_server verbose:(bool) f_verbose;
+- (void) setUniversalModeTimeout:(double) f_timeout;
+
+-(void) handleEvent:(eATVClientEvent) f_event;
+-(void) switchRemote:(int) f_device_id;
+
+- (void) enableVerboseMode:(bool) f_really;
+@end \ No newline at end of file
diff --git a/tools/EventClients/Clients/OSXRemote/xbmcclientwrapper.mm b/tools/EventClients/Clients/OSXRemote/xbmcclientwrapper.mm
new file mode 100644
index 0000000000..ab4d0de655
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/xbmcclientwrapper.mm
@@ -0,0 +1,430 @@
+/*
+ * xbmcclient.cpp
+ * xbmclauncher
+ *
+ * Created by Stephan Diederich on 17.09.08.
+ * Copyright 2008 University Heidelberg. All rights reserved.
+ *
+ */
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include "xbmcclientwrapper.h"
+#include "../../lib/c++/xbmcclient.h"
+#include "XBMCDebugHelpers.h"
+#include <vector>
+#include <map>
+#include <string>
+#include <sstream>
+
+//helper class for easy EventSequence handling
+class XBMCClientEventSequence{
+public:
+ XBMCClientEventSequence(){}
+
+ //implicit conversion
+ XBMCClientEventSequence(eATVClientEvent f_event){
+ m_stream.push_back(f_event);
+ }
+
+ std::string str() const{
+ std::stringstream ss;
+ for(std::vector<eATVClientEvent>::const_iterator it = m_stream.begin();
+ it != m_stream.end();
+ ++it){
+ ss << *it;
+ }
+ return ss.str();
+ }
+ void clear(){
+ m_stream.clear();
+ }
+
+ //
+ // operators
+ //
+ friend XBMCClientEventSequence operator+ (XBMCClientEventSequence f_seq, eATVClientEvent f_event){
+ f_seq.m_stream.push_back(f_event);
+ return f_seq;
+ }
+ XBMCClientEventSequence& operator << (eATVClientEvent f_event){
+ m_stream.push_back(f_event);
+ return *this;
+ }
+ friend bool operator <(XBMCClientEventSequence const& fcr_lhs,XBMCClientEventSequence const& fcr_rhs){
+ return fcr_lhs.m_stream < fcr_rhs.m_stream;
+ }
+ friend bool operator ==(XBMCClientEventSequence const& fcr_lhs,XBMCClientEventSequence const& fcr_rhs){
+ return fcr_lhs.m_stream == fcr_rhs.m_stream;
+ }
+private:
+ std::vector<eATVClientEvent> m_stream;
+};
+
+
+//typedef is here, as is seems that I can't put it into iterface declaration
+//CPacketBUTTON is a pointer, as I'm not sure how well it's copy constructor is implemented
+typedef std::map<eATVClientEvent, CPacketBUTTON*> tEventMap;
+typedef std::map<XBMCClientEventSequence, CPacketBUTTON*> tSequenceMap;
+typedef std::map<std::pair<int, eATVClientEvent>, CPacketBUTTON*> tMultiRemoteMap;
+
+class XBMCClientWrapperImpl{
+ tEventMap m_event_map;
+ tSequenceMap m_sequence_map;
+ tMultiRemoteMap m_multiremote_map;
+ eRemoteMode m_mode;
+ int m_socket;
+ std::string m_address;
+ XBMCClientEventSequence m_sequence;
+ CFRunLoopTimerRef m_timer;
+ double m_sequence_timeout;
+ int m_device_id;
+ bool m_verbose_mode;
+ void populateEventMap();
+ void populateSequenceMap();
+ void populateMultiRemoteModeMap();
+ void sendButton(eATVClientEvent f_event);
+ void sendSequence();
+ void restartTimer();
+ void resetTimer();
+ bool isStartToken(eATVClientEvent f_event);
+ static void timerCallBack (CFRunLoopTimerRef timer, void *info);
+public:
+ XBMCClientWrapperImpl(eRemoteMode f_mode, const std::string& fcr_address = "localhost", bool f_verbose_mode=false);
+ ~XBMCClientWrapperImpl();
+ void setUniversalModeTimeout(double f_timeout){
+ m_sequence_timeout = f_timeout;
+ }
+ void switchRemote(int f_device_id){
+ m_device_id = f_device_id;
+ }
+ void handleEvent(eATVClientEvent f_event);
+ void enableVerboseMode(bool f_value){
+ m_verbose_mode = f_value;
+ }
+};
+
+void XBMCClientWrapperImpl::timerCallBack (CFRunLoopTimerRef timer, void *info)
+{
+ if (!info)
+ {
+ fprintf(stderr, "Error. invalid argument to timer callback\n");
+ return;
+ }
+
+ XBMCClientWrapperImpl *p_impl = (XBMCClientWrapperImpl *)info;
+ p_impl->sendSequence();
+ p_impl->resetTimer();
+}
+
+void XBMCClientWrapperImpl::resetTimer(){
+ if (m_timer)
+ {
+ CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), m_timer, kCFRunLoopCommonModes);
+ CFRunLoopTimerInvalidate(m_timer);
+ CFRelease(m_timer);
+ m_timer = NULL;
+ }
+}
+
+void XBMCClientWrapperImpl::restartTimer(){
+ if (m_timer)
+ resetTimer();
+
+ CFRunLoopTimerContext context = { 0, this, 0, 0, 0 };
+ m_timer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + m_sequence_timeout, 0, 0, 0, timerCallBack, &context);
+ CFRunLoopAddTimer(CFRunLoopGetCurrent(), m_timer, kCFRunLoopCommonModes);
+}
+
+XBMCClientWrapperImpl::XBMCClientWrapperImpl(eRemoteMode f_mode, const std::string& fcr_address, bool f_verbose_mode):
+m_mode(f_mode), m_address(fcr_address), m_timer(0), m_sequence_timeout(0.5), m_device_id(150), m_verbose_mode(f_verbose_mode){
+ PRINT_SIGNATURE();
+
+ if(m_mode == MULTIREMOTE_MODE){
+ if(m_verbose_mode)
+ NSLog(@"XBMCClientWrapperImpl started in multiremote mode sending to address %s", fcr_address.c_str());
+ populateMultiRemoteModeMap();
+ } else {
+ if(m_mode == UNIVERSAL_MODE){
+ if(m_verbose_mode)
+ NSLog(@"XBMCClientWrapperImpl started in universal mode sending to address %s", fcr_address.c_str());
+ populateSequenceMap();
+ } else if(m_verbose_mode)
+ NSLog(@"XBMCClientWrapperImpl started in normal mode sending to address %s", fcr_address.c_str());
+ populateEventMap();
+ }
+
+ //open udp port etc
+ m_socket = socket(AF_INET, SOCK_DGRAM, 0);
+ if (m_socket < 0)
+ {
+ ELOG(@"Error opening UDP socket! error: ", errno);
+ //TODO What to do?
+ }
+}
+
+namespace {
+ struct delete_second{
+ template <class T>
+ void operator ()(T& fr_pair){
+ delete fr_pair.second;
+ }
+ };
+}
+XBMCClientWrapperImpl::~XBMCClientWrapperImpl(){
+ PRINT_SIGNATURE();
+ resetTimer();
+ shutdown(m_socket, SHUT_RDWR);
+ std::for_each(m_event_map.begin(), m_event_map.end(), delete_second());
+ std::for_each(m_sequence_map.begin(), m_sequence_map.end(), delete_second());
+ std::for_each(m_multiremote_map.begin(), m_multiremote_map.end(), delete_second());
+}
+
+bool XBMCClientWrapperImpl::isStartToken(eATVClientEvent f_event){
+ return f_event==ATV_BUTTON_MENU_H;
+}
+
+void XBMCClientWrapperImpl::sendButton(eATVClientEvent f_event){
+ CPacketBUTTON* lp_packet = 0;
+ if(m_mode == MULTIREMOTE_MODE){
+ tMultiRemoteMap::iterator it = m_multiremote_map.find(std::make_pair(m_device_id, f_event));
+ if(it == m_multiremote_map.end()){
+ ELOG(@"XBMCClientWrapperImpl::sendButton: No mapping defined for remoteID: %i button %i", m_device_id, f_event);
+ return;
+ }
+ lp_packet = it->second;
+ } else {
+ tEventMap::iterator it = m_event_map.find(f_event);
+ if(it == m_event_map.end()){
+ ELOG(@"XBMCClientWrapperImpl::sendButton: No mapping defined for button %i", f_event);
+ return;
+ }
+ lp_packet = it->second;
+ }
+ assert(lp_packet);
+ CAddress addr(m_address.c_str());
+ if(m_verbose_mode)
+ NSLog(@"XBMCClientWrapperImpl::sendButton sending button %i down:%i up:%i", lp_packet->GetButtonCode(), lp_packet->GetFlags()&BTN_DOWN,lp_packet->GetFlags()&BTN_UP );
+ lp_packet->Send(m_socket, addr);
+}
+
+void XBMCClientWrapperImpl::sendSequence(){
+ tSequenceMap::const_iterator it = m_sequence_map.find(m_sequence);
+ if(it != m_sequence_map.end()){
+ CPacketBUTTON& packet = *(it->second);
+ CAddress addr(m_address.c_str());
+ packet.Send(m_socket, addr);
+ if(m_verbose_mode)
+ NSLog(@"XBMCClientWrapperImpl::sendSequence sent sequence %i down:%i up:%i", packet.GetButtonCode(), packet.GetFlags()&BTN_DOWN,packet.GetFlags()&BTN_UP );
+ } else {
+ ELOG(@"XBMCClientWrapperImpl::sendSequence: No mapping defined for sequence %s", m_sequence.str().c_str());
+ }
+ m_sequence.clear();
+}
+
+void XBMCClientWrapperImpl::handleEvent(eATVClientEvent f_event){
+ if(m_mode != UNIVERSAL_MODE){
+ sendButton(f_event);
+ } else {
+ //in universal mode no keys are directly send. instead a key-sequence is assembled and a timer started
+ //when the timer expires, that key sequence is checked against predefined sequences and if it is a valid one,
+ //a button press is generated
+ if(m_sequence.str().empty()){
+ if(isStartToken(f_event)){
+ m_sequence << f_event;
+ DLOG(@"Starting sequence with token %s", m_sequence.str().c_str());
+ restartTimer();
+ } else {
+ sendButton(f_event);
+ }
+ } else {
+ //dont queue release-events but restart timer
+ if(f_event == ATV_BUTTON_LEFT_RELEASE || f_event == ATV_BUTTON_RIGHT_RELEASE || f_event == ATV_BUTTON_UP_RELEASE || f_event == ATV_BUTTON_DOWN_RELEASE)
+ DLOG(@"Discarded button up event for sequence");
+ else{
+ m_sequence << f_event;
+ DLOG(@"Extended sequence to %s", m_sequence.str().c_str());
+ }
+ restartTimer();
+ }
+ }
+}
+
+void XBMCClientWrapperImpl::populateEventMap(){
+ tEventMap& lr_map = m_event_map;
+
+ lr_map.insert(std::make_pair(ATV_BUTTON_PLAY, new CPacketBUTTON(5, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_RIGHT, new CPacketBUTTON(4, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_RIGHT_RELEASE, new CPacketBUTTON(4, "JS0:AppleRemote", BTN_UP | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_LEFT, new CPacketBUTTON(3, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT| BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_LEFT_RELEASE, new CPacketBUTTON(3, "JS0:AppleRemote", BTN_UP | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_MENU, new CPacketBUTTON(6, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_MENU_H, new CPacketBUTTON(8, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_UP, new CPacketBUTTON(1, "JS0:AppleRemote", BTN_DOWN | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_UP_RELEASE, new CPacketBUTTON(1, "JS0:AppleRemote", BTN_UP | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_DOWN, new CPacketBUTTON(2, "JS0:AppleRemote", BTN_DOWN | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_DOWN_RELEASE, new CPacketBUTTON(2, "JS0:AppleRemote", BTN_UP | BTN_QUEUE)));
+
+ // only present on ATV <= 2.1 <--- check that; OSX seems to have the release parts
+ lr_map.insert(std::make_pair(ATV_BUTTON_RIGHT_H, new CPacketBUTTON(10, "JS0:AppleRemote", BTN_DOWN | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_RIGHT_H_RELEASE, new CPacketBUTTON(10, "JS0:AppleRemote", BTN_UP | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_LEFT_H, new CPacketBUTTON(11, "JS0:AppleRemote", BTN_DOWN | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_BUTTON_LEFT_H_RELEASE, new CPacketBUTTON(11, "JS0:AppleRemote", BTN_UP | BTN_QUEUE)));
+
+ // only present on atv >= 2.2
+ lr_map.insert(std::make_pair(ATV_BUTTON_PLAY_H, new CPacketBUTTON(7, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+
+ //learned remote buttons (ATV >=2.3)
+ lr_map.insert(std::make_pair(ATV_LEARNED_PLAY, new CPacketBUTTON(70, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_LEARNED_PAUSE, new CPacketBUTTON(71, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_LEARNED_STOP, new CPacketBUTTON(72, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_LEARNED_PREVIOUS, new CPacketBUTTON(73, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_LEARNED_NEXT, new CPacketBUTTON(74, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_LEARNED_REWIND, new CPacketBUTTON(75, "JS0:AppleRemote", BTN_DOWN | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_LEARNED_REWIND_RELEASE, new CPacketBUTTON(75, "JS0:AppleRemote", BTN_UP | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_LEARNED_FORWARD, new CPacketBUTTON(76, "JS0:AppleRemote", BTN_DOWN | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_LEARNED_FORWARD_RELEASE, new CPacketBUTTON(76, "JS0:AppleRemote", BTN_UP | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_LEARNED_RETURN, new CPacketBUTTON(77, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ lr_map.insert(std::make_pair(ATV_LEARNED_ENTER, new CPacketBUTTON(78, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+}
+
+void XBMCClientWrapperImpl::populateSequenceMap(){
+ XBMCClientEventSequence sequence_prefix;
+ sequence_prefix << ATV_BUTTON_MENU_H;
+ m_sequence_map.insert(std::make_pair(sequence_prefix, new CPacketBUTTON(8, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_PLAY, new CPacketBUTTON(20, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_RIGHT, new CPacketBUTTON(21, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_LEFT, new CPacketBUTTON(22, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_UP, new CPacketBUTTON(23, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_DOWN, new CPacketBUTTON(24, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_MENU, new CPacketBUTTON(25, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+
+ sequence_prefix.clear();
+ sequence_prefix << ATV_BUTTON_MENU_H << ATV_BUTTON_PLAY;
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_PLAY, new CPacketBUTTON(26, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_RIGHT, new CPacketBUTTON(27, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_LEFT, new CPacketBUTTON(28, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_UP, new CPacketBUTTON(29, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_DOWN, new CPacketBUTTON(30, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_MENU, new CPacketBUTTON(31, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+
+ sequence_prefix.clear();
+ sequence_prefix << ATV_BUTTON_MENU_H << ATV_BUTTON_UP;
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_PLAY, new CPacketBUTTON(32, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_RIGHT, new CPacketBUTTON(33, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_LEFT, new CPacketBUTTON(34, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_UP, new CPacketBUTTON(35, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_DOWN, new CPacketBUTTON(36, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_MENU, new CPacketBUTTON(37, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+
+ sequence_prefix.clear();
+ sequence_prefix << ATV_BUTTON_MENU_H << ATV_BUTTON_DOWN;
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_PLAY, new CPacketBUTTON(38, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_RIGHT, new CPacketBUTTON(39, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_LEFT, new CPacketBUTTON(40, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_UP, new CPacketBUTTON(41, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_DOWN, new CPacketBUTTON(42, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_MENU, new CPacketBUTTON(43, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+
+ sequence_prefix.clear();
+ sequence_prefix << ATV_BUTTON_MENU_H << ATV_BUTTON_RIGHT;
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_PLAY, new CPacketBUTTON(44, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_RIGHT, new CPacketBUTTON(45, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_LEFT, new CPacketBUTTON(46, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_UP, new CPacketBUTTON(47, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_DOWN, new CPacketBUTTON(48, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_MENU, new CPacketBUTTON(49, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+
+ sequence_prefix.clear();
+ sequence_prefix << ATV_BUTTON_MENU_H << ATV_BUTTON_LEFT;
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_PLAY, new CPacketBUTTON(50, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_RIGHT, new CPacketBUTTON(51, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_LEFT, new CPacketBUTTON(52, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_UP, new CPacketBUTTON(53, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_DOWN, new CPacketBUTTON(54, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_sequence_map.insert(std::make_pair( sequence_prefix + ATV_BUTTON_MENU, new CPacketBUTTON(55, "JS0:AppleRemote", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+}
+
+void XBMCClientWrapperImpl::populateMultiRemoteModeMap(){
+ //the example of harmony as a multi-remote uses a few key device-id's paired with the normal buttons
+ static int device_ids[] = {150, 151, 152, 153, 154, 155, 157, 158, 159, 160};
+ int offset = 0;
+ for(int* device_id = device_ids; device_id != device_ids + sizeof(device_ids)/sizeof(*device_ids); ++device_id, offset += 10)
+ {
+ // keymaps for mult-apple-remote, including the device-key sent after remote-switch
+ // we just add them here with unique button numbers and do the real mapping in keymap.xml
+ // as an offset for the buttons we use the device
+
+ // this loop should probably be replaced by a proper setting of the individual keys
+ // cons: currently only button-codes (aka ints) are sent
+ // way too lazy ;)
+ // pro: custom tweaks. e.g. button 1 on the harmony may be (153, ATV_BUTTON_LEFT) and this should not get a repeat
+ // maybe use the loop and tweak individual buttons later; plex maps here to strings, and later in keymap.xml to other strings,
+ // but this may need another kind of remote in XBMC source
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_UP), new CPacketBUTTON(1 + offset, "JS0:Harmony", BTN_DOWN | BTN_QUEUE)));
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_UP_RELEASE), new CPacketBUTTON(1 + offset, "JS0:Harmony", BTN_UP | BTN_QUEUE)));
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_DOWN), new CPacketBUTTON(2 + offset, "JS0:Harmony", BTN_DOWN | BTN_QUEUE)));
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_DOWN_RELEASE), new CPacketBUTTON(2 + offset, "JS0:Harmony", BTN_UP | BTN_QUEUE)));
+
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_LEFT), new CPacketBUTTON(3 + offset, "JS0:Harmony", BTN_DOWN | BTN_QUEUE)));
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_LEFT_RELEASE), new CPacketBUTTON(3 + offset, "JS0:Harmony", BTN_UP | BTN_QUEUE)));
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_RIGHT), new CPacketBUTTON(4 + offset, "JS0:Harmony", BTN_DOWN | BTN_QUEUE)));
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_RIGHT_RELEASE), new CPacketBUTTON(4 + offset, "JS0:Harmony", BTN_UP | BTN_QUEUE)));
+
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_PLAY), new CPacketBUTTON(5 + offset, "JS0:Harmony", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_MENU), new CPacketBUTTON(6 + offset, "JS0:Harmony", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_PLAY_H), new CPacketBUTTON(7 + offset, "JS0:Harmony", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_MENU_H), new CPacketBUTTON(8 + offset, "JS0:Harmony", BTN_DOWN | BTN_NO_REPEAT | BTN_QUEUE)));
+
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_RIGHT_H), new CPacketBUTTON(9 + offset, "JS0:Harmony", BTN_DOWN | BTN_QUEUE)));
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_RIGHT_H_RELEASE),new CPacketBUTTON(9 + offset, "JS0:Harmony", BTN_UP | BTN_QUEUE)));
+
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_LEFT_H), new CPacketBUTTON(10 + offset, "JS0:Harmony", BTN_DOWN | BTN_QUEUE)));
+ m_multiremote_map.insert(std::make_pair(std::make_pair(*device_id,ATV_BUTTON_LEFT_H_RELEASE),new CPacketBUTTON(10 + offset, "JS0:Harmony", BTN_UP | BTN_QUEUE)));
+ }
+}
+
+@implementation XBMCClientWrapper
+- (id) init {
+ return [self initWithMode:DEFAULT_MODE serverAddress:@"localhost" verbose: false];
+}
+- (id) initWithMode:(eRemoteMode) f_mode serverAddress:(NSString*) fp_server verbose:(bool) f_verbose{
+ PRINT_SIGNATURE();
+ if( ![super init] )
+ return nil;
+ mp_impl = new XBMCClientWrapperImpl(f_mode, [fp_server UTF8String], f_verbose);
+ return self;
+}
+
+- (void) setUniversalModeTimeout:(double) f_timeout{
+ mp_impl->setUniversalModeTimeout(f_timeout);
+}
+
+- (void)dealloc{
+ PRINT_SIGNATURE();
+ delete mp_impl;
+ [super dealloc];
+}
+
+-(void) handleEvent:(eATVClientEvent) f_event{
+ mp_impl->handleEvent(f_event);
+}
+
+-(void) switchRemote:(int) f_device_id{
+ mp_impl->switchRemote(f_device_id);
+}
+
+- (void) enableVerboseMode:(bool) f_really{
+ mp_impl->enableVerboseMode(f_really);
+}
+@end
diff --git a/tools/EventClients/Clients/OSXRemote/xbmchelper_main.mm b/tools/EventClients/Clients/OSXRemote/xbmchelper_main.mm
new file mode 100644
index 0000000000..d4af31501a
--- /dev/null
+++ b/tools/EventClients/Clients/OSXRemote/xbmchelper_main.mm
@@ -0,0 +1,210 @@
+#include "Carbon/Carbon.h"
+#import "XBMCHelper.h"
+#include <getopt.h>
+#include <string>
+#include <vector>
+#include <sstream>
+#include <fstream>
+#include <iterator>
+
+using namespace std;
+
+//instantiate XBMCHelper which registers itself to IR handling stuff
+XBMCHelper* gp_xbmchelper;
+eRemoteMode g_mode = DEFAULT_MODE;
+std::string g_server_address="localhost";
+std::string g_app_path = "";
+std::string g_app_home = "";
+double g_universal_timeout = 0.500;
+bool g_verbose_mode = false;
+
+//
+const char* PROGNAME="XBMCHelper";
+const char* PROGVERS="0.5";
+
+void ParseOptions(int argc, char** argv);
+void ReadConfig();
+
+static struct option long_options[] = {
+{ "help", no_argument, 0, 'h' },
+{ "server", required_argument, 0, 's' },
+{ "universal", no_argument, 0, 'u' },
+{ "multiremote", no_argument, 0, 'm' },
+{ "timeout", required_argument, 0, 't' },
+{ "verbose", no_argument, 0, 'v' },
+{ "externalConfig", no_argument, 0, 'x' },
+{ "appPath", required_argument, 0, 'a' },
+{ "appHome", required_argument, 0, 'z' },
+{ 0, 0, 0, 0 },
+};
+
+static const char *options = "hs:umt:vxa:z:";
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+void usage(void)
+{
+ printf("%s (version %s)\n", PROGNAME, PROGVERS);
+ printf(" Sends Apple Remote events to XBMC.\n\n");
+ printf("Usage: %s [OPTIONS...]\n\nOptions:\n", PROGNAME);
+ printf(" -h, --help print this help message and exit.\n");
+ printf(" -s, --server <addr> send events to the specified IP.\n");
+ printf(" -u, --universal runs in Universal Remote mode.\n");
+ printf(" -t, --timeout <ms> timeout length for sequences (default: 500ms).\n");
+ printf(" -m, --multiremote runs in Multi-Remote mode (adds remote identifier as additional idenfier to buttons)\n");
+ printf(" -a, --appPath path to XBMC.app (MenuPress launch support).\n");
+ printf(" -z, --appHome path to XBMC.app/Content/Resources/XBMX \n");
+ printf(" -v, --verbose prints lots of debugging information.\n");
+}
+
+//----------------------------------------------------------------------------
+void ReadConfig()
+{
+ // Compute filename.
+ std::string strFile = getenv("HOME");
+ strFile += "/Library/Application Support/XBMC/XBMCHelper.conf";
+
+ // Open file.
+ std::ifstream ifs(strFile.c_str());
+ if (!ifs)
+ return;
+
+ // Read file.
+ stringstream oss;
+ oss << ifs.rdbuf();
+
+ if (!ifs && !ifs.eof())
+ return;
+
+ // Tokenize.
+ string strData(oss.str());
+ istringstream is(strData);
+ vector<string> args = vector<string>(istream_iterator<string>(is), istream_iterator<string>());
+
+ // Convert to char**.
+ int argc = args.size() + 1;
+ char** argv = new char*[argc + 1];
+ int i = 0;
+ argv[i++] = "XBMCHelper";
+
+ for (vector<string>::iterator it = args.begin(); it != args.end(); ){
+ //fixup the arguments, here: remove '"' like bash would normally do
+ std::string::size_type j = 0;
+ while ((j = it->find("\"", j)) != std::string::npos )
+ it->replace(j, 1, "");
+ argv[i++] = (char* )(*it++).c_str();
+ }
+
+ argv[i] = 0;
+
+ // Parse the arguments.
+ ParseOptions(argc, argv);
+
+ delete[] argv;
+}
+
+//----------------------------------------------------------------------------
+void ParseOptions(int argc, char** argv)
+{
+ int c, option_index = 0;
+ //set the defaults
+ bool readExternal = false;
+ g_server_address = "localhost";
+ g_mode = DEFAULT_MODE;
+ g_app_path = "";
+ g_app_home = "";
+ g_universal_timeout = 0.5;
+ g_verbose_mode = false;
+
+ while ((c = getopt_long(argc, argv, options, long_options, &option_index)) != -1)
+ {
+ switch (c) {
+ case 'h':
+ usage();
+ exit(0);
+ break;
+ case 'v':
+ g_verbose_mode = true;
+ break;
+ case 's':
+ g_server_address = optarg;
+ break;
+ case 'u':
+ g_mode = UNIVERSAL_MODE;
+ break;
+ case 'm':
+ g_mode = MULTIREMOTE_MODE;
+ break;
+ case 't':
+ g_universal_timeout = atof(optarg) * 0.001;
+ break;
+ case 'x':
+ readExternal = true;
+ break;
+ case 'a':
+ g_app_path = optarg;
+ break;
+ case 'z':
+ g_app_home = optarg;
+ break;
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+ //reset getopts state
+ optreset = 1;
+ optind = 0;
+
+ if (readExternal == true)
+ ReadConfig();
+
+}
+
+//----------------------------------------------------------------------------
+void StartHelper(){
+ [gp_xbmchelper enableVerboseMode:g_verbose_mode];
+
+ //set apppath to startup when pressing Menu
+ [gp_xbmchelper setApplicationPath:[NSString stringWithCString:g_app_path.c_str()]];
+ //set apppath to startup when pressing Menu
+ [gp_xbmchelper setApplicationHome:[NSString stringWithCString:g_app_home.c_str()]];
+ //connect to specified server
+ [gp_xbmchelper connectToServer:[NSString stringWithCString:g_server_address.c_str()] withMode:g_mode withTimeout: g_universal_timeout];
+}
+
+//----------------------------------------------------------------------------
+void Reconfigure(int nSignal)
+{
+ if (nSignal == SIGHUP){
+ ReadConfig();
+ StartHelper();
+ }
+ else {
+ QuitEventLoop(GetMainEventLoop());
+ }
+}
+
+//----------------------------------------------------------------------------
+int main (int argc, char * argv[]) {
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+ NSLog(@"%s %s starting up...", PROGNAME, PROGVERS);
+ gp_xbmchelper = [[XBMCHelper alloc] init];
+
+ signal(SIGHUP, Reconfigure);
+ signal(SIGINT, Reconfigure);
+ signal(SIGTERM, Reconfigure);
+
+ ParseOptions(argc,argv);
+ StartHelper();
+
+ //run event loop in this thread
+ RunCurrentEventLoop(kEventDurationForever);
+ NSLog(@"%s %s exiting...", PROGNAME, PROGVERS);
+ //cleanup
+ [gp_xbmchelper release];
+ [pool drain];
+ return 0;
+}
diff --git a/tools/EventClients/Clients/PS3 BD Remote/ps3_remote.py b/tools/EventClients/Clients/PS3 BD Remote/ps3_remote.py
new file mode 100755
index 0000000000..c6b2aea4a1
--- /dev/null
+++ b/tools/EventClients/Clients/PS3 BD Remote/ps3_remote.py
@@ -0,0 +1,202 @@
+#!/usr/bin/python
+
+# This is a quick port of brandonj's PS3 remote script to use the event server
+# for sending input events.
+#
+# The original script and documentation regarding the remote can be found at:
+# http://xbmc.org/forum/showthread.php?t=28765
+#
+#
+# TODO:
+# 1. Send keepalive ping at least once every 60 seconds to prevent timeouts
+# 2. Permanent pairing
+# 3. Detect if XBMC has been restarted (non trivial until broadcasting is
+# implemented, until then maybe the HELO packet could be used instead of
+# PING as keepalive
+#
+
+import sys
+
+try:
+ # try loading modules from source directory
+ sys.path.append("../../lib/python")
+ from xbmcclient import *
+ from ps3.keymaps import keymap_remote as g_keymap # look here to change the keymapping
+ from bt.bt import *
+ ICON_PATH = "../../icons/"
+except:
+ # fallback to system wide modules
+ from xbmc.xbmcclient import *
+ from xbmc.ps3.keymaps import keymap_remote as g_keymap # look here to change the keymapping
+ from xbmc.bt.bt import *
+ from xbmc.defs import *
+
+import os
+import time
+
+xbmc = None
+bticon = ICON_PATH + "/bluetooth.png"
+
+def get_remote_address(remote, target_name = "BD Remote Control"):
+ global xbmc
+ target_connected = False
+ target_address = None
+ while target_connected is False:
+ xbmc.send_notification("Action Required!",
+ "Hold Start+Enter on your remote.",
+ bticon)
+ print "Searching for %s" % target_name
+ print "(Hold Start + Enter on remote to make it discoverable)"
+ time.sleep(2)
+
+ if not target_address:
+ try:
+ nearby_devices = bt_discover_devices()
+ except Exception, e:
+ print "Error performing bluetooth discovery"
+ print str(e)
+ xbmc.send_notification("Error", "Unable to find devices.", bticon)
+ time.sleep(5)
+ continue
+
+ for bdaddr in nearby_devices:
+ bname = bt_lookup_name( bdaddr )
+ addr = bt_lookup_addr ( bdaddr )
+ print "%s (%s) in range" % (bname,addr)
+ if target_name == bname:
+ target_address = addr
+ break
+
+ if target_address is not None:
+ print "Found %s with address %s" % (target_name, target_address)
+ xbmc.send_notification("Found Device",
+ "Pairing %s, please wait." % target_name,
+ bticon)
+ print "Attempting to pair with remote"
+
+ try:
+ remote.connect((target_address,19))
+ target_connected = True
+ print "Remote Paired.\a"
+ xbmc.send_notification("Pairing Successfull",
+ "Your remote was successfully "\
+ "paired and is ready to be used.",
+ bticon)
+ except:
+ del remote
+ remote = bt_create_socket()
+ target_address = None
+ xbmc.send_notification("Pairing Failed",
+ "An error occurred while attempting to "\
+ "pair.", bticon)
+ print "ERROR - Could Not Connect. Trying again..."
+ time.sleep(2)
+ else:
+ xbmc.send_notification("Error", "No remotes were found.", bticon)
+ print "Could not find BD Remote Control. Trying again..."
+ time.sleep(2)
+ return (remote,target_address)
+
+
+def usage():
+ print """
+PS3 Blu-Ray Remote Control Client for XBMC v0.1
+
+Usage: ps3_remote.py <address> [port]
+
+ address => address of system that XBMC is running on
+ ("localhost" if it is this machine)
+
+ port => port to send packets to
+ (default 9777)
+"""
+
+def process_keys(remote, xbmc):
+ """
+ Return codes:
+ 0 - key was processed normally
+ 2 - socket read timeout
+ 3 - PS and then Skip Plus was pressed (sequentially)
+ 4 - PS and then Skip Minus was pressed (sequentially)
+
+ FIXME: move to enums
+ """
+ done = 0
+
+ try:
+ xbmc.previous_key
+ except:
+ xbmc.previous_key = ""
+
+ xbmc.connect()
+ datalen = 0
+ try:
+ data = remote.recv(1024)
+ datalen = len(data)
+ except Exception, e:
+ if str(e)=="timed out":
+ return 2
+ time.sleep(2)
+
+ # some other read exception occured, so raise it
+ raise e
+
+ if datalen == 13:
+ keycode = data.encode("hex")[10:12]
+ if keycode == "ff":
+ xbmc.release_button()
+ return done
+ try:
+ # if the user presses the PS button followed by skip + or skip -
+ # return different codes.
+ if xbmc.previous_key == "43":
+ xbmc.previous_key = keycode
+ if keycode == "31": # skip +
+ return 3
+ elif keycode == "30": # skip -
+ return 4
+
+ # save previous key press
+ xbmc.previous_key = keycode
+
+ if g_keymap[keycode]:
+ xbmc.send_remote_button(g_keymap[keycode])
+ except Exception, e:
+ print "Unknown data: %s" % str(e)
+ return done
+
+def main():
+ global xbmc, bticon
+
+ host = "127.0.0.1"
+ port = 9777
+
+ if len(sys.argv)>1:
+ try:
+ host = sys.argv[1]
+ port = sys.argv[2]
+ except:
+ pass
+ else:
+ return usage()
+
+ loop_forever = True
+ xbmc = XBMCClient("PS3 Bluetooth Remote",
+ icon_file=bticon)
+
+ while loop_forever is True:
+ target_connected = False
+ remote = bt_create_socket()
+ xbmc.connect(host, port)
+ (remote,target_address) = get_remote_address(remote)
+ while True:
+ if process_keys(remote, xbmc):
+ break
+ print "Disconnected."
+ try:
+ remote.close()
+ except:
+ print "Cannot close."
+
+if __name__=="__main__":
+ main()
diff --git a/tools/EventClients/Clients/PS3 Sixaxis Controller/ps3d.py b/tools/EventClients/Clients/PS3 Sixaxis Controller/ps3d.py
new file mode 100755
index 0000000000..b28f9ba9da
--- /dev/null
+++ b/tools/EventClients/Clients/PS3 Sixaxis Controller/ps3d.py
@@ -0,0 +1,433 @@
+#!/usr/bin/python
+
+import sys
+import traceback
+import time
+import struct
+import threading
+
+sys.path.append("../PS3 BD Remote")
+
+try:
+ # try loading modules from source directory
+ sys.path.append("../../lib/python")
+ from bt.hid import HID
+ from bt.bt import bt_lookup_name
+ from xbmcclient import XBMCClient
+ from ps3 import sixaxis
+ from ps3.keymaps import keymap_sixaxis
+ from ps3_remote import process_keys as process_remote
+ try:
+ import zeroconf
+ except:
+ zeroconf = None
+ ICON_PATH = "../../icons/"
+except:
+ # fallback to system wide modules
+ from xbmc.bt.hid import HID
+ from xbmc.bt.bt import bt_lookup_name
+ from xbmc.xbmcclient import XBMCClient
+ from xbmc.ps3 import sixaxis
+ from xbmc.ps3.keymaps import keymap_sixaxis
+ from xbmc.ps3_remote import process_keys as process_remote
+ from xbmc.defs import *
+ try:
+ import xbmc.zeroconf as zeroconf
+ except:
+ zeroconf = None
+
+
+event_threads = []
+
+def printerr():
+ trace = ""
+ exception = ""
+ exc_list = traceback.format_exception_only (sys.exc_type, sys.exc_value)
+ for entry in exc_list:
+ exception += entry
+ tb_list = traceback.format_tb(sys.exc_info()[2])
+ for entry in tb_list:
+ trace += entry
+ print("%s\n%s" % (exception, trace), "Script Error")
+
+
+class StoppableThread ( threading.Thread ):
+ def __init__(self):
+ threading.Thread.__init__(self)
+ self._stop = False
+ self.set_timeout(0)
+
+ def stop_thread(self):
+ self._stop = True
+
+ def stop(self):
+ return self._stop
+
+ def close_sockets(self):
+ if self.isock:
+ try:
+ self.isock.close()
+ except:
+ pass
+ self.isock = None
+ if self.csock:
+ try:
+ self.csock.close()
+ except:
+ pass
+ self.csock = None
+ self.last_action = 0
+
+ def set_timeout(self, seconds):
+ self.timeout = seconds
+
+ def reset_timeout(self):
+ self.last_action = time.time()
+
+ def idle_time(self):
+ return time.time() - self.last_action
+
+ def timed_out(self):
+ if (time.time() - self.last_action) > self.timeout:
+ return True
+ else:
+ return False
+
+# to make sure all combination keys are checked first
+# we sort the keymap's button codes in reverse order
+# this guranties that any bit combined button code
+# will be processed first
+keymap_sixaxis_keys = keymap_sixaxis.keys()
+keymap_sixaxis_keys.sort()
+keymap_sixaxis_keys.reverse()
+
+def getkeys(bflags):
+ keys = [];
+ for k in keymap_sixaxis_keys:
+ if (k & bflags) == k:
+ keys.append(k)
+ bflags = bflags & ~k
+ return keys;
+
+class PS3SixaxisThread ( StoppableThread ):
+ def __init__(self, csock, isock, ipaddr="127.0.0.1"):
+ StoppableThread.__init__(self)
+ self.csock = csock
+ self.isock = isock
+ self.xbmc = XBMCClient(name="PS3 Sixaxis", icon_file=ICON_PATH + "/bluetooth.png", ip=ipaddr)
+ self.set_timeout(600)
+
+ def run(self):
+ sixaxis.initialize(self.csock, self.isock)
+ self.xbmc.connect()
+ bflags = 0
+ released = set()
+ pressed = set()
+ pending = set()
+ held = set()
+ psflags = 0
+ psdown = 0
+ toggle_mouse = 0
+ self.reset_timeout()
+ try:
+ while not self.stop():
+ if self.timed_out():
+
+ for key in (held | pressed):
+ (mapname, action, amount, axis) = keymap_sixaxis[key]
+ self.xbmc.send_button_state(map=mapname, button=action, amount=0, down=0, axis=axis)
+
+ raise Exception("PS3 Sixaxis powering off, timed out")
+ if self.idle_time() > 50:
+ self.xbmc.connect()
+ try:
+ data = sixaxis.read_input(self.isock)
+ except Exception, e:
+ print str(e)
+ break
+ if not data:
+ continue
+
+ (bflags, psflags, pressure) = sixaxis.process_input(data, self.xbmc, toggle_mouse)
+
+ if psflags:
+ self.reset_timeout()
+ if psdown:
+ if (time.time() - psdown) > 5:
+
+ for key in (held | pressed):
+ (mapname, action, amount, axis) = keymap_sixaxis[key]
+ self.xbmc.send_button_state(map=mapname, button=action, amount=0, down=0, axis=axis)
+
+ raise Exception("PS3 Sixaxis powering off, user request")
+ else:
+ psdown = time.time()
+ else:
+ if psdown:
+ toggle_mouse = 1 - toggle_mouse
+ psdown = 0
+
+ keys = set(getkeys(bflags))
+ released = (pressed | held) - keys
+ held = (pressed | held) - released
+ pressed = (keys - held) & pending
+ pending = (keys - held)
+
+ for key in released:
+ (mapname, action, amount, axis) = keymap_sixaxis[key]
+ self.xbmc.send_button_state(map=mapname, button=action, amount=0, down=0, axis=axis)
+
+ for key in held:
+ (mapname, action, amount, axis) = keymap_sixaxis[key]
+ if amount > 0:
+ amount = pressure[amount-1] * 256
+ self.xbmc.send_button_state(map=mapname, button=action, amount=amount, down=1, axis=axis)
+
+ for key in pressed:
+ (mapname, action, amount, axis) = keymap_sixaxis[key]
+ if amount > 0:
+ amount = pressure[amount-1] * 256
+ self.xbmc.send_button_state(map=mapname, button=action, amount=amount, down=1, axis=axis)
+
+ if keys:
+ self.reset_timeout()
+
+
+ except Exception, e:
+ printerr()
+ self.close_sockets()
+
+
+class PS3RemoteThread ( StoppableThread ):
+ def __init__(self, csock, isock, ipaddr="127.0.0.1"):
+ StoppableThread.__init__(self)
+ self.csock = csock
+ self.isock = isock
+ self.xbmc = XBMCClient(name="PS3 Blu-Ray Remote", icon_file=ICON_PATH + "/bluetooth.png", ip=ipaddr)
+ self.set_timeout(600)
+ self.services = []
+ self.current_xbmc = 0
+
+ def run(self):
+ sixaxis.initialize(self.csock, self.isock)
+ self.xbmc.connect()
+ try:
+ # start the zeroconf thread if possible
+ try:
+ self.zeroconf_thread = ZeroconfThread()
+ self.zeroconf_thread.add_service('_xbmc-events._udp',
+ self.zeroconf_service_handler)
+ self.zeroconf_thread.start()
+ except Exception, e:
+ print str(e)
+
+ # main thread loop
+ while not self.stop():
+ status = process_remote(self.isock, self.xbmc)
+
+ if status == 2: # 2 = socket read timeout
+ if self.timed_out():
+ raise Exception("PS3 Blu-Ray Remote powering off, "\
+ "timed out")
+ elif status == 3: # 3 = ps and skip +
+ self.next_xbmc()
+
+ elif status == 4: # 4 = ps and skip -
+ self.previous_xbmc()
+
+ elif not status: # 0 = keys are normally processed
+ self.reset_timeout()
+
+ # process_remote() will raise an exception on read errors
+ except Exception, e:
+ print str(e)
+
+ self.zeroconf_thread.stop()
+ self.close_sockets()
+
+ def next_xbmc(self):
+ """
+ Connect to the next XBMC instance
+ """
+ self.current_xbmc = (self.current_xbmc + 1) % len( self.services )
+ self.reconnect()
+ return
+
+ def previous_xbmc(self):
+ """
+ Connect to the previous XBMC instance
+ """
+ self.current_xbmc -= 1
+ if self.current_xbmc < 0 :
+ self.current_xbmc = len( self.services ) - 1
+ self.reconnect()
+ return
+
+ def reconnect(self):
+ """
+ Reconnect to an XBMC instance based on self.current_xbmc
+ """
+ try:
+ service = self.services[ self.current_xbmc ]
+ print "Connecting to %s" % service['name']
+ self.xbmc.connect( service['address'], service['port'] )
+ self.xbmc.send_notification("PS3 Blu-Ray Remote", "New Connection", None)
+ except Exception, e:
+ print str(e)
+
+ def zeroconf_service_handler(self, event, service):
+ """
+ Zeroconf event handler
+ """
+ if event == zeroconf.SERVICE_FOUND: # new xbmc service detected
+ self.services.append( service )
+
+ elif event == zeroconf.SERVICE_LOST: # xbmc service lost
+ try:
+ # search for the service by name, since IP+port isn't available
+ for s in self.services:
+ # nuke it, if found
+ if service['name'] == s['name']:
+ self.services.remove(s)
+ break
+ except:
+ pass
+ return
+
+
+class ZeroconfThread ( threading.Thread ):
+ """
+
+ """
+ def __init__(self):
+ threading.Thread.__init__(self)
+ self._zbrowser = None
+ self._services = []
+
+ def run(self):
+ if zeroconf:
+ # create zeroconf service browser
+ self._zbrowser = zeroconf.Browser()
+
+ # add the requested services
+ for service in self._services:
+ self._zbrowser.add_service( service[0], service[1] )
+
+ # run the event loop
+ self._zbrowser.run()
+
+ return
+
+
+ def stop(self):
+ """
+ Stop the zeroconf browser
+ """
+ try:
+ self._zbrowser.stop()
+ except:
+ pass
+ return
+
+ def add_service(self, type, handler):
+ """
+ Add a new service to search for.
+ NOTE: Services must be added before thread starts.
+ """
+ self._services.append( [ type, handler ] )
+
+
+def usage():
+ print """
+PS3 Sixaxis / Blu-Ray Remote HID Server v0.1
+
+Usage: ps3.py [bdaddress] [XBMC host]
+
+ bdaddress => address of local bluetooth device to use (default: auto)
+ (e.g. aa:bb:cc:dd:ee:ff)
+ ip address => IP address or hostname of the XBMC instance (default: localhost)
+ (e.g. 192.168.1.110)
+"""
+
+def start_hidd(bdaddr=None, ipaddr="127.0.0.1"):
+ devices = [ 'PLAYSTATION(R)3 Controller',
+ 'BD Remote Control' ]
+ hid = HID(bdaddr)
+ while True:
+ if hid.listen():
+ (csock, addr) = hid.get_control_socket()
+ device_name = bt_lookup_name(addr[0])
+ if device_name == devices[0]:
+ # handle PS3 controller
+ handle_ps3_controller(hid, ipaddr)
+ elif device_name == devices[1]:
+ # handle the PS3 remote
+ handle_ps3_remote(hid, ipaddr)
+ else:
+ print "Unknown Device: %s" % (device_name)
+
+def handle_ps3_controller(hid, ipaddr):
+ print "Received connection from a Sixaxis PS3 Controller"
+ csock = hid.get_control_socket()[0]
+ isock = hid.get_interrupt_socket()[0]
+ sixaxis = PS3SixaxisThread(csock, isock, ipaddr)
+ add_thread(sixaxis)
+ sixaxis.start()
+ return
+
+def handle_ps3_remote(hid, ipaddr):
+ print "Received connection from a PS3 Blu-Ray Remote"
+ csock = hid.get_control_socket()[0]
+ isock = hid.get_interrupt_socket()[0]
+ isock.settimeout(1)
+ remote = PS3RemoteThread(csock, isock, ipaddr)
+ add_thread(remote)
+ remote.start()
+ return
+
+def add_thread(thread):
+ global event_threads
+ event_threads.append(thread)
+
+def main():
+ if len(sys.argv)>3:
+ return usage()
+ bdaddr = ""
+ ipaddr = "127.0.0.1"
+ try:
+ for addr in sys.argv[1:]:
+ try:
+ # ensure that the addr is of the format 'aa:bb:cc:dd:ee:ff'
+ if "".join([ str(len(a)) for a in addr.split(":") ]) != "222222":
+ raise Exception("Invalid format")
+ bdaddr = addr
+ print "Connecting to Bluetooth device: %s" % bdaddr
+ except Exception, e:
+ try:
+ ipaddr = addr
+ print "Connecting to : %s" % ipaddr
+ except:
+ print str(e)
+ return usage()
+ except Exception, e:
+ pass
+
+ print "Starting HID daemon"
+ start_hidd(bdaddr, ipaddr)
+
+if __name__=="__main__":
+ try:
+ main()
+ finally:
+ for t in event_threads:
+ try:
+ print "Waiting for thread "+str(t)+" to terminate"
+ t.stop_thread()
+ if t.isAlive():
+ t.join()
+ print "Thread "+str(t)+" terminated"
+
+ except Exception, e:
+ print str(e)
+ pass
+
diff --git a/tools/EventClients/Clients/WiiRemote/CWIID_WiiRemote.cpp b/tools/EventClients/Clients/WiiRemote/CWIID_WiiRemote.cpp
new file mode 100644
index 0000000000..08ba318f86
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/CWIID_WiiRemote.cpp
@@ -0,0 +1,774 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Tobias Arrskog,,, *
+ * topfs@tobias *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+// Compiles with g++ WiiRemote.cpp -lcwiid -o WiiRemote
+// Preferably with libcwiid >= 6.0
+
+#include "CWIID_WiiRemote.h"
+
+
+bool g_AllowReconnect = true;
+bool g_AllowMouse = true;
+bool g_AllowNunchuck = true;
+
+CPacketHELO *g_Ping = NULL;
+
+#ifndef ICON_PATH
+#define ICON_PATH "../../"
+#endif
+std::string g_BluetoothIconPath = std::string(ICON_PATH) + std::string("/bluetooth.png");
+
+long getTicks()
+{
+ long ticks;
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ ticks = now.tv_sec * 1000l;
+ ticks += now.tv_usec / 1000l;
+ return ticks;
+}
+
+CWiiRemote g_WiiRemote;
+
+#ifdef CWIID_OLD
+void CWiiRemote::MessageCallback(cwiid_wiimote_t *wiiremote, int mesg_count, union cwiid_mesg mesg[])
+{
+ MessageCallback(wiiremote, mesg_count, mesg, NULL);
+}
+#endif
+
+/* The MessageCallback for the Wiiremote.
+ This callback is used for error reports, mainly to see if the connection has been broken
+ This callback is also used for getting the IR sources, if this is done in update as with buttons we usually only get 1 IR source at a time wich is much harder to calculate */
+void CWiiRemote::MessageCallback(cwiid_wiimote_t *wiiremote, int mesg_count, union cwiid_mesg mesg[], struct timespec *timestamp)
+{
+ for (int i=0; i < mesg_count; i++)
+ {
+ int valid_source;
+ switch (mesg[i].type)
+ {
+ case CWIID_MESG_IR:
+ valid_source = 0;
+ for (int j = 0; j < CWIID_IR_SRC_COUNT; j++)
+ {
+ if (mesg[i].ir_mesg.src[j].valid)
+ valid_source++;
+ }
+ if (valid_source == 2)
+ {
+ g_WiiRemote.CalculateMousePointer(mesg[i].ir_mesg.src[0].pos[CWIID_X],
+ mesg[i].ir_mesg.src[0].pos[CWIID_Y],
+ mesg[i].ir_mesg.src[1].pos[CWIID_X],
+ mesg[i].ir_mesg.src[1].pos[CWIID_Y]);
+ }
+ else if (valid_source > 2)
+ { //TODO Make this care with the strenght of the sources
+ g_WiiRemote.CalculateMousePointer(mesg[i].ir_mesg.src[0].pos[CWIID_X],
+ mesg[i].ir_mesg.src[0].pos[CWIID_Y],
+ mesg[i].ir_mesg.src[1].pos[CWIID_X],
+ mesg[i].ir_mesg.src[1].pos[CWIID_Y]);
+ }
+ break;
+ case CWIID_MESG_ERROR:
+ g_WiiRemote.DisconnectNow(true);
+ break;
+ case CWIID_MESG_BTN:
+ g_WiiRemote.ProcessKey(mesg[i].btn_mesg.buttons);
+ break;
+ case CWIID_MESG_STATUS:
+ //Here we can figure out Extensiontypes and such
+ break;
+ case CWIID_MESG_NUNCHUK:
+ g_WiiRemote.ProcessNunchuck(mesg[i].nunchuk_mesg);
+ break;
+ case CWIID_MESG_CLASSIC:
+ //Not implemented
+ break;
+ case CWIID_MESG_ACC:
+ //Not implemented
+ break;
+ case CWIID_MESG_UNKNOWN:
+ //...
+ break;
+ }
+ }
+#ifdef CWIID_OLD
+ g_WiiRemote.CheckIn();
+#endif
+}
+
+#ifndef _DEBUG
+/* This takes the errors generated at pre-connect and silence them as they are mostly not needed */
+void CWiiRemote::ErrorCallback(struct wiimote *wiiremote, const char *str, va_list ap)
+{
+ //Error checking
+}
+#endif
+
+//Constructor
+/*This constructor is never used but it shows how one would connect to just a specific Wiiremote by Mac-Adress*/
+CWiiRemote::CWiiRemote(char *wii_btaddr)
+{
+ SetBluetoothAddress(wii_btaddr);
+ m_SamplesX = NULL;
+ m_SamplesY = NULL;
+
+ m_JoyMap = NULL;
+}
+
+//Destructor
+CWiiRemote::~CWiiRemote()
+{
+ if (m_connected == true)
+ this->DisconnectNow(false);
+
+ if (m_SamplesY != NULL)
+ free(m_SamplesY);
+ if (m_SamplesX != NULL)
+ free(m_SamplesX);
+
+ if (m_JoyMap)
+ free(m_JoyMap);
+}
+
+//---------------------Public-------------------------------------------------------------------
+/* Basicly this just sets up standard control bits */
+void CWiiRemote::SetBluetoothAddress(const char *btaddr)
+{
+ if (btaddr != NULL)
+ str2ba(btaddr, &m_btaddr);
+ else
+ bacpy(&m_btaddr, &(*BDADDR_ANY));
+}
+
+void CWiiRemote::SetSensativity(float DeadX, float DeadY, int NumSamples)
+{
+ m_NumSamples = NumSamples;
+
+ m_MinX = MOUSE_MAX * DeadX;
+ m_MinY = MOUSE_MAX * DeadY;
+
+ m_MaxX = MOUSE_MAX * (1.0f + DeadX + DeadX);
+ m_MaxY = MOUSE_MAX * (1.0f + DeadY + DeadY);
+
+ if (m_SamplesY != NULL)
+ delete [] m_SamplesY;
+ if (m_SamplesX != NULL)
+ delete [] m_SamplesX;
+
+ m_SamplesY = new int[m_NumSamples];
+ m_SamplesX = new int[m_NumSamples];
+}
+
+void CWiiRemote::SetJoystickMap(const char *JoyMap)
+{
+ if (m_JoyMap)
+ free(m_JoyMap);
+ if (JoyMap != NULL)
+ {
+ m_JoyMap = new char[strlen(JoyMap) + 4];
+ sprintf(m_JoyMap, "JS0:%s", JoyMap);
+ }
+ else
+ {
+ m_JoyMap = new char[strlen("JS0:WiiRemote")];
+ strcpy(m_JoyMap, "JS0:WiiRemote");
+ }
+}
+
+void CWiiRemote::Initialize(CAddress Addr, int Socket)
+{
+ m_connected = false;
+ m_lastKeyPressed = 0;
+ m_LastKey = 0;
+ m_buttonRepeat = false;
+ m_lastKeyPressedNunchuck = 0;
+ m_LastKeyNunchuck = 0;
+ m_buttonRepeatNunchuck = false;
+ m_useIRMouse = true;
+ m_rptMode = 0;
+
+ m_Socket = Socket;
+ m_MyAddr = Addr;
+
+ m_NumSamples = WIIREMOTE_SAMPLES;
+
+ m_MaxX = WIIREMOTE_X_MAX;
+ m_MaxY = WIIREMOTE_Y_MAX;
+ m_MinX = WIIREMOTE_X_MIN;
+ m_MinY = WIIREMOTE_Y_MIN;
+#ifdef CWIID_OLD
+ m_LastMsgTime = getTicks();
+#endif
+
+ //All control bits are set to false when cwiid is started
+ //Report Button presses
+ ToggleBit(m_rptMode, CWIID_RPT_BTN);
+ if (g_AllowNunchuck)
+ ToggleBit(m_rptMode, CWIID_RPT_NUNCHUK);
+
+ //If wiiremote is used as a mouse, then report the IR sources
+#ifndef CWIID_OLD
+ if (m_useIRMouse)
+#endif
+ ToggleBit(m_rptMode, CWIID_RPT_IR);
+
+ //Have the first and fourth LED on the Wiiremote shine when connected
+ ToggleBit(m_ledState, CWIID_LED1_ON);
+ ToggleBit(m_ledState, CWIID_LED4_ON);
+}
+
+/* Update is run regulary and we gather the state of the Wiiremote and see if the user have pressed on a button or moved the wiiremote
+ This could have been done with callbacks instead but it doesn't look nice in C++*/
+void CWiiRemote::Update()
+{
+ if (m_DisconnectWhenPossible)
+ {//If the user have choosen to disconnect or lost comunication
+ DisconnectNow(true);
+ m_DisconnectWhenPossible = false;
+ }
+#ifdef CWIID_OLD
+ if(m_connected)
+ {//Here we check if the connection is suddenly broken
+ if (!CheckConnection())
+ {
+ DisconnectNow(true);
+ return;
+ }
+ }
+#endif
+}
+
+/* Enable mouse emulation */
+void CWiiRemote::EnableMouseEmulation()
+{
+ if (m_useIRMouse)
+ return;
+
+ m_useIRMouse = true;
+
+#ifndef CWIID_OLD
+ //We toggle IR Reporting (Save resources?)
+ if (!(m_rptMode & CWIID_RPT_IR))
+ ToggleBit(m_rptMode, CWIID_RPT_IR);
+ if (m_connected)
+ SetRptMode();
+#endif
+
+ CPacketLOG log(LOGDEBUG, "Enabled WiiRemote mouse emulation");
+ log.Send(m_Socket, m_MyAddr);
+}
+/* Disable mouse emulation */
+void CWiiRemote::DisableMouseEmulation()
+{
+ if (!m_useIRMouse)
+ return;
+
+ m_useIRMouse = false;
+#ifndef CWIID_OLD
+ //We toggle IR Reporting (Save resources?)
+ if (m_rptMode & CWIID_RPT_IR)
+ ToggleBit(m_rptMode, CWIID_RPT_IR);
+ if (m_connected)
+ SetRptMode();
+#endif
+
+ CPacketLOG log(LOGDEBUG, "Disabled WiiRemote mouse emulation");
+ log.Send(m_Socket, m_MyAddr);
+}
+
+/* Is a wiiremote connected*/
+bool CWiiRemote::GetConnected()
+{
+ return m_connected;
+}
+
+/* Disconnect ASAP*/
+void CWiiRemote::Disconnect()
+{ //This is always called from a criticalsection
+ if (m_connected)
+ m_DisconnectWhenPossible = true;
+}
+
+#ifdef CWIID_OLD
+/* This function is mostly a hack as CWIID < 6.0 doesn't report on disconnects, this function is called everytime
+ a message is sent to the callback (Will be once every 10 ms or so) this is to see if the connection is interupted. */
+void CWiiRemote::CheckIn()
+{ //This is always called from a criticalsection
+ m_LastMsgTime = getTicks();
+}
+#endif
+
+//---------------------Private-------------------------------------------------------------------
+/* Connect is designed to be run in a different thread as it only
+ exits if wiiremote is either disabled or a connection is made*/
+bool CWiiRemote::Connect()
+{
+#ifndef _DEBUG
+ cwiid_set_err(ErrorCallback);
+#endif
+ while (!m_connected)
+ {
+ g_Ping->Send(m_Socket, m_MyAddr);
+ int flags = 0;
+ ToggleBit(flags, CWIID_FLAG_MESG_IFC);
+ ToggleBit(flags, CWIID_FLAG_REPEAT_BTN);
+
+ m_wiiremoteHandle = cwiid_connect(&m_btaddr, flags);
+ if (m_wiiremoteHandle != NULL)
+ {
+ SetupWiiRemote();
+ // get battery state etc.
+ cwiid_state wiiremote_state;
+ int err = cwiid_get_state(m_wiiremoteHandle, &wiiremote_state);
+ if (!err)
+ {
+ char Mesg[1024];
+ sprintf(Mesg, "%i%% battery remaining", static_cast<int>(((float)(wiiremote_state.battery)/CWIID_BATTERY_MAX)*100.0));
+ CPacketNOTIFICATION notification("Wii Remote connected", Mesg, ICON_PNG, g_BluetoothIconPath.c_str());
+ notification.Send(m_Socket, m_MyAddr);
+ }
+ else
+ {
+ printf("Problem probing for status of WiiRemote; cwiid_get_state returned non-zero\n");
+ CPacketLOG log(LOGNOTICE, "Problem probing for status of WiiRemote; cwiid_get_state returned non-zero");
+ log.Send(m_Socket, m_MyAddr);
+ CPacketNOTIFICATION notification("Wii Remote connected", "", ICON_PNG, g_BluetoothIconPath.c_str());
+ notification.Send(m_Socket, m_MyAddr);
+ }
+#ifdef CWIID_OLD
+ /* CheckIn to say that this is the last msg, If this isn't called it could give issues if we Connects -> Disconnect and then try to connect again
+ the CWIID_OLD hack would automaticly disconnect the wiiremote as the lastmsg is too old. */
+ CheckIn();
+#endif
+ m_connected = true;
+
+ CPacketLOG log(LOGNOTICE, "Sucessfully connected a WiiRemote");
+ log.Send(m_Socket, m_MyAddr);
+ return true;
+ }
+ //Here's a good place to have a quit flag check...
+
+ }
+ return false;
+}
+
+/* Disconnect */
+void CWiiRemote::DisconnectNow(bool startConnectThread)
+{
+ if (m_connected) //It shouldn't be enabled at the same time as it is connected
+ {
+ cwiid_disconnect(m_wiiremoteHandle);
+
+ if (g_AllowReconnect)
+ {
+ CPacketNOTIFICATION notification("Wii Remote disconnected", "Press 1 and 2 to reconnect", ICON_PNG, g_BluetoothIconPath.c_str());
+ notification.Send(m_Socket, m_MyAddr);
+ }
+ else
+ {
+ CPacketNOTIFICATION notification("Wii Remote disconnected", "", ICON_PNG, g_BluetoothIconPath.c_str());
+ notification.Send(m_Socket, m_MyAddr);
+ }
+
+ CPacketLOG log(LOGNOTICE, "Sucessfully disconnected a WiiRemote");
+ log.Send(m_Socket, m_MyAddr);
+ }
+ m_connected = false;
+}
+
+#ifdef CWIID_OLD
+/* This is a harsh check if there really is a connection, It will mainly be used in CWIID < 6.0
+ as it doesn't report connect error, wich is needed to see if the Wiiremote suddenly disconnected.
+ This could possible be done with bluetooth specific queries but I cannot find how to do it. */
+bool CWiiRemote::CheckConnection()
+{
+ if ((getTicks() - m_LastMsgTime) > 1000)
+ {
+ CPacketLOG log(LOGNOTICE, "Lost connection to the WiiRemote");
+ log.Send(m_Socket, m_MyAddr);
+ return false;
+ }
+ else
+ return true;
+}
+#endif
+
+/* Sets rpt mode when a new wiiremote is connected */
+void CWiiRemote::SetupWiiRemote()
+{ //Lights up the apropriate led and setups the rapport mode, so buttons and IR work
+ SetRptMode();
+ SetLedState();
+
+ for (int i = 0; i < WIIREMOTE_SAMPLES; i++)
+ {
+ m_SamplesX[i] = 0;
+ m_SamplesY[i] = 0;
+ }
+
+ if (cwiid_set_mesg_callback(m_wiiremoteHandle, MessageCallback))
+ {
+ CPacketLOG log(LOGERROR, "Unable to set message callback to the WiiRemote");
+ log.Send(m_Socket, m_MyAddr);
+ }
+}
+
+void CWiiRemote::ProcessKey(int Key)
+{
+ if (Key != m_LastKey)
+ {
+ m_LastKey = Key;
+ m_lastKeyPressed = getTicks();
+ m_buttonRepeat = false;
+ }
+ else
+ {
+ if (m_buttonRepeat)
+ {
+ if (getTicks() - m_lastKeyPressed > WIIREMOTE_BUTTON_REPEAT_TIME)
+ m_lastKeyPressed = getTicks();
+ else
+ return;
+ }
+ else
+ {
+ if (getTicks() - m_lastKeyPressed > WIIREMOTE_BUTTON_DELAY_TIME)
+ {
+ m_buttonRepeat = true;
+ m_lastKeyPressed = getTicks();
+ }
+ else
+ return;
+ }
+ }
+
+ int RtnKey = -1;
+
+ if (Key == CWIID_BTN_UP)
+ RtnKey = 1;
+ else if (Key == CWIID_BTN_RIGHT)
+ RtnKey = 4;
+ else if (Key == CWIID_BTN_LEFT)
+ RtnKey = 3;
+ else if (Key == CWIID_BTN_DOWN)
+ RtnKey = 2;
+
+ else if (Key == CWIID_BTN_A)
+ RtnKey = 5;
+ else if (Key == CWIID_BTN_B)
+ RtnKey = 6;
+
+ else if (Key == CWIID_BTN_MINUS)
+ RtnKey = 7;
+ else if (Key == CWIID_BTN_PLUS)
+ RtnKey = 9;
+
+ else if (Key == CWIID_BTN_HOME)
+ RtnKey = 8;
+
+ else if (Key == CWIID_BTN_1)
+ RtnKey = 10;
+ else if (Key == CWIID_BTN_2)
+ RtnKey = 11;
+
+ if (RtnKey != -1)
+ {
+ CPacketBUTTON btn(RtnKey, m_JoyMap, BTN_QUEUE | BTN_NO_REPEAT);
+ btn.Send(m_Socket, m_MyAddr);
+ }
+}
+
+void CWiiRemote::ProcessNunchuck(struct cwiid_nunchuk_mesg &Nunchuck)
+{
+ if (Nunchuck.stick[0] > 135)
+ { //R
+ int x = (int)((((float)Nunchuck.stick[0] - 135.0f) / 95.0f) * 65535.0f);
+ printf("Right: %i\n", x);
+ CPacketBUTTON btn(24, m_JoyMap, (BTN_QUEUE | BTN_DOWN), x);
+ btn.Send(m_Socket, m_MyAddr);
+ }
+ else if (Nunchuck.stick[0] < 125)
+ { //L
+ int x = (int)((((float)Nunchuck.stick[0] - 125.0f) / 90.0f) * -65535.0f);
+ printf("Left: %i\n", x);
+ CPacketBUTTON btn(23, m_JoyMap, (BTN_QUEUE | BTN_DOWN), x);
+ btn.Send(m_Socket, m_MyAddr);
+ }
+
+ if (Nunchuck.stick[1] > 130)
+ { //U
+ int x = (int)((((float)Nunchuck.stick[1] - 130.0f) / 92.0f) * 65535.0f);
+ printf("Up: %i\n", x);
+ CPacketBUTTON btn(21, m_JoyMap, (BTN_QUEUE | BTN_DOWN), x);
+ btn.Send(m_Socket, m_MyAddr);
+ }
+ else if (Nunchuck.stick[1] < 120)
+ { //D
+ int x = (int)((((float)Nunchuck.stick[1] - 120.0f) / 90.0f) * -65535.0f);
+ printf("Down: %i\n", x);
+ CPacketBUTTON btn(22, m_JoyMap, (BTN_QUEUE | BTN_DOWN), x);
+ btn.Send(m_Socket, m_MyAddr);
+ }
+
+ if (Nunchuck.buttons != m_LastKeyNunchuck)
+ {
+ m_LastKeyNunchuck = Nunchuck.buttons;
+ m_lastKeyPressedNunchuck = getTicks();
+ m_buttonRepeatNunchuck = false;
+ }
+ else
+ {
+ if (m_buttonRepeatNunchuck)
+ {
+ if (getTicks() - m_lastKeyPressedNunchuck > WIIREMOTE_BUTTON_REPEAT_TIME)
+ m_lastKeyPressedNunchuck = getTicks();
+ else
+ return;
+ }
+ else
+ {
+ if (getTicks() - m_lastKeyPressedNunchuck > WIIREMOTE_BUTTON_DELAY_TIME)
+ {
+ m_buttonRepeatNunchuck = true;
+ m_lastKeyPressedNunchuck = getTicks();
+ }
+ else
+ return;
+ }
+ }
+
+ int RtnKey = -1;
+
+ if (Nunchuck.buttons == CWIID_NUNCHUK_BTN_C)
+ RtnKey = 25;
+ else if (Nunchuck.buttons == CWIID_NUNCHUK_BTN_Z)
+ RtnKey = 26;
+
+ if (RtnKey != -1)
+ {
+ CPacketBUTTON btn(RtnKey, m_JoyMap, BTN_QUEUE | BTN_NO_REPEAT);
+ btn.Send(m_Socket, m_MyAddr);
+ }
+}
+
+/* Tell cwiid wich data will be reported */
+void CWiiRemote::SetRptMode()
+{ //Sets our wiiremote to report something, for example IR, Buttons
+#ifdef CWIID_OLD
+ if (cwiid_command(m_wiiremoteHandle, CWIID_CMD_RPT_MODE, m_rptMode))
+#else
+ if (cwiid_set_rpt_mode(m_wiiremoteHandle, m_rptMode))
+#endif
+ {
+ CPacketLOG log(LOGERROR, "Error setting WiiRemote report mode");
+ log.Send(m_Socket, m_MyAddr);
+ }
+}
+/* Tell cwiid the LED states */
+void CWiiRemote::SetLedState()
+{ //Sets our leds on the wiiremote
+#ifdef CWIID_OLD
+ if (cwiid_command(m_wiiremoteHandle, CWIID_CMD_LED, m_ledState))
+#else
+ if (cwiid_set_led(m_wiiremoteHandle, m_ledState))
+#endif
+ {
+ CPacketLOG log(LOGERROR, "Error setting WiiRemote LED state");
+ log.Send(m_Socket, m_MyAddr);
+ }
+}
+
+/* Calculate the mousepointer from 2 IR sources (Default) */
+void CWiiRemote::CalculateMousePointer(int x1, int y1, int x2, int y2)
+{
+ int x3, y3;
+
+ x3 = ( (x1 + x2) / 2 );
+ y3 = ( (y1 + y2) / 2 );
+
+ x3 = (int)( ((float)x3 / (float)CWIID_IR_X_MAX) * m_MaxX);
+ y3 = (int)( ((float)y3 / (float)CWIID_IR_Y_MAX) * m_MaxY);
+
+ x3 = (int)(x3 - m_MinX);
+ y3 = (int)(y3 - m_MinY);
+
+ if (x3 < MOUSE_MIN) x3 = MOUSE_MIN;
+ else if (x3 > MOUSE_MAX) x3 = MOUSE_MAX;
+
+ if (y3 < MOUSE_MIN) y3 = MOUSE_MIN;
+ else if (y3 > MOUSE_MAX) y3 = MOUSE_MAX;
+
+ x3 = MOUSE_MAX - x3;
+
+ if (m_NumSamples == 1)
+ {
+ CPacketMOUSE mouse(x3, y3);
+ mouse.Send(m_Socket, m_MyAddr);
+ return;
+ }
+ else
+ {
+ for (int i = m_NumSamples; i > 0; i--)
+ {
+ m_SamplesX[i] = m_SamplesX[i-1];
+ m_SamplesY[i] = m_SamplesY[i-1];
+ }
+
+ m_SamplesX[0] = x3;
+ m_SamplesY[0] = y3;
+
+ long x4 = 0, y4 = 0;
+
+ for (int i = 0; i < m_NumSamples; i++)
+ {
+ x4 += m_SamplesX[i];
+ y4 += m_SamplesY[i];
+ }
+ CPacketMOUSE mouse((x4 / m_NumSamples), (y4 / m_NumSamples));
+ mouse.Send(m_Socket, m_MyAddr);
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void PrintHelp(const char *Prog)
+{
+ printf("Commands:\n");
+ printf("\t--disable-mouseemulation\n\t--disable-reconnect\n\t--disable-nunchuck\n");
+ printf("\t--address ADDRESS\n\t--port PORT\n");
+ printf("\t--btaddr MACADDRESS\n");
+ printf("\t--deadzone-x DEADX | Number between 0 - 100 (Default: %i)\n", (int)(DEADZONE_X * 100));
+ printf("\t--deadzone-y DEADY | Number between 0 - 100 (Default: %i)\n", (int)(DEADZONE_Y * 100));
+ printf("\t--deadzone DEAD | Sets both X and Y too the number\n");
+ printf("\t--smoothing-samples SAMPLE | Number 1 counts as Off (Default: %i)\n", WIIREMOTE_SAMPLES);
+ printf("\t--joystick-map JOYMAP | The string ID for the joymap (Default: WiiRemote)\n");
+}
+
+int main(int argc, char **argv)
+{
+ char *Address = NULL;
+ char *btaddr = NULL;
+ int Port = 9777;
+
+ int NumSamples = WIIREMOTE_SAMPLES;
+ float DeadX = DEADZONE_X;
+ float DeadY = DEADZONE_Y;
+
+ char *JoyMap = NULL;
+
+ for (int i = 0; i < argc; i++)
+ {
+ if (strcmp(argv[i], "--help") == 0)
+ {
+ PrintHelp(argv[0]);
+ return 0;
+ }
+ else if (strcmp(argv[i], "--disable-mouseemulation") == 0)
+ g_AllowMouse = false;
+ else if (strcmp(argv[i], "--disable-reconnect") == 0)
+ g_AllowReconnect = false;
+ else if (strcmp(argv[i], "--disable-nunchuck") == 0)
+ g_AllowNunchuck = false;
+ else if (strcmp(argv[i], "--address") == 0 && ((i + 1) <= argc))
+ Address = argv[i + 1];
+ else if (strcmp(argv[i], "--port") == 0 && ((i + 1) <= argc))
+ Port = atoi(argv[i + 1]);
+ else if (strcmp(argv[i], "--btaddr") == 0 && ((i + 1) <= argc))
+ btaddr = argv[i + 1];
+ else if (strcmp(argv[i], "--deadzone-x") == 0 && ((i + 1) <= argc))
+ DeadX = ((float)atoi(argv[i + 1]) / 100.0f);
+ else if (strcmp(argv[i], "--deadzone-y") == 0 && ((i + 1) <= argc))
+ DeadY = ((float)atoi(argv[i + 1]) / 100.0f);
+ else if (strcmp(argv[i], "--deadzone") == 0 && ((i + 1) <= argc))
+ DeadX = DeadY = ((float)atoi(argv[i + 1]) / 100.0f);
+ else if (strcmp(argv[i], "--smoothing-samples") == 0 && ((i + 1) <= argc))
+ NumSamples = atoi(argv[i + 1]);
+ else if (strcmp(argv[i], "--joystick-map") == 0 && ((i + 1) <= argc))
+ JoyMap = argv[i + 1];
+ }
+
+ if (NumSamples < 1 || DeadX < 0 || DeadY < 0 || DeadX > 1 || DeadY > 1)
+ {
+ PrintHelp(argv[0]);
+ return -1;
+ }
+
+ CAddress my_addr(Address, Port); // Address => localhost on 9777
+ int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ {
+ printf("Error creating socket\n");
+ return -1;
+ }
+
+ if (hci_get_route(NULL) < 0)
+ {
+ CPacketLOG log(LOGERROR, "Error No bluetooth device");
+ log.Send(sockfd, my_addr);
+ return -1;
+ }
+ g_Ping = new CPacketHELO("WiiRemote", ICON_PNG, g_BluetoothIconPath.c_str());
+ g_WiiRemote.Initialize(my_addr, sockfd);
+ g_WiiRemote.SetBluetoothAddress(btaddr);
+ g_WiiRemote.SetSensativity(DeadX, DeadY, NumSamples);
+ g_WiiRemote.SetSensativity(DeadX, DeadY, NumSamples);
+ g_WiiRemote.SetJoystickMap(JoyMap);
+ if (g_AllowMouse)
+ g_WiiRemote.EnableMouseEmulation();
+ else
+ g_WiiRemote.DisableMouseEmulation();
+
+ g_Ping->Send(sockfd, my_addr);
+ bool HaveConnected = false;
+ while (true)
+ {
+ bool Connected = g_WiiRemote.GetConnected();
+
+ while (!Connected)
+ {
+ if (HaveConnected && !g_AllowReconnect)
+ exit(0);
+
+ Connected = g_WiiRemote.Connect();
+ HaveConnected = true;
+ }
+#ifdef CWIID_OLD
+// Update the state of the WiiRemote more often when we have the old lib due too it not telling when disconnected..
+ sleep (5);
+#else
+ sleep (15);
+#endif
+ g_Ping->Send(sockfd, my_addr);
+ g_WiiRemote.Update();
+ }
+}
diff --git a/tools/EventClients/Clients/WiiRemote/CWIID_WiiRemote.h b/tools/EventClients/Clients/WiiRemote/CWIID_WiiRemote.h
new file mode 100644
index 0000000000..87e9010e6f
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/CWIID_WiiRemote.h
@@ -0,0 +1,170 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Tobias Arrskog,,, *
+ * topfs@tobias *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef WII_REMOTE_H
+#define WII_REMOTE_H
+
+/* Toggle one bit */
+#define ToggleBit(bf,b) (bf) = ((bf) & b) ? ((bf) & ~(b)) : ((bf) | (b))
+
+//Settings
+#define WIIREMOTE_SAMPLES 16
+
+#define DEADZONE_Y 0.3f
+#define DEADZONE_X 0.5f
+
+#define MOUSE_MAX 65535
+#define MOUSE_MIN 0
+
+//The work area is from 0 - MAX but the one sent to XBMC is MIN - (MIN + MOUSE_MAX)
+#define WIIREMOTE_X_MIN MOUSE_MAX * DEADZONE_X
+#define WIIREMOTE_Y_MIN MOUSE_MAX * DEADZONE_Y
+
+#define WIIREMOTE_X_MAX MOUSE_MAX * (1.0f + DEADZONE_X + DEADZONE_X)
+#define WIIREMOTE_Y_MAX MOUSE_MAX * (1.0f + DEADZONE_Y + DEADZONE_Y)
+
+#define WIIREMOTE_BUTTON_REPEAT_TIME 30 // How long between buttonpresses in repeat mode
+#define WIIREMOTE_BUTTON_DELAY_TIME 500
+//#define CWIID_OLD // Uncomment if the system is running cwiid that is older than 6.0 (The one from ubuntu gutsy repository is < 6.0)
+
+//CWIID
+#include <cwiid.h>
+//Bluetooth specific
+#include <sys/socket.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+// UDP Client
+#ifdef DEB_PACK
+#include <xbmc/xbmcclient.h>
+#else
+#include "../../lib/c++/xbmcclient.h"
+#endif
+/*#include <stdio.h>*/
+#include <stdlib.h>
+#include <time.h>
+#include <sys/time.h>
+#include <string>
+
+class CWiiRemote
+{
+public:
+ CWiiRemote(char *btaddr = NULL);
+ ~CWiiRemote();
+
+ void Initialize(CAddress Addr, int Socket);
+ void Disconnect();
+ bool GetConnected();
+
+ bool EnableWiiRemote();
+ bool DisableWiiRemote();
+
+ void Update();
+
+ // Mouse functions
+ bool HaveIRSources();
+ bool isActive();
+ void EnableMouseEmulation();
+ void DisableMouseEmulation();
+
+ bool Connect();
+
+ void SetBluetoothAddress(const char * btaddr);
+ void SetSensativity(float DeadX, float DeadY, int Samples);
+ void SetJoystickMap(const char *JoyMap);
+private:
+ int m_NumSamples;
+ int *m_SamplesX;
+ int *m_SamplesY;
+
+ float m_MaxX;
+ float m_MaxY;
+ float m_MinX;
+ float m_MinY;
+#ifdef CWIID_OLD
+ bool CheckConnection();
+ int m_LastMsgTime;
+#endif
+ char *m_JoyMap;
+ int m_lastKeyPressed;
+ int m_LastKey;
+ bool m_buttonRepeat;
+
+ int m_lastKeyPressedNunchuck;
+ int m_LastKeyNunchuck;
+ bool m_buttonRepeatNunchuck;
+
+ void SetRptMode();
+ void SetLedState();
+
+ void SetupWiiRemote();
+
+ bool m_connected;
+
+ bool m_DisconnectWhenPossible;
+ bool m_connectThreadRunning;
+
+ //CWIID Specific
+ cwiid_wiimote_t *m_wiiremoteHandle;
+ unsigned char m_ledState;
+ unsigned char m_rptMode;
+ bdaddr_t m_btaddr;
+
+ static void MessageCallback(cwiid_wiimote_t *wiiremote, int mesgCount, union cwiid_mesg mesg[], struct timespec *timestamp);
+#ifdef CWIID_OLD
+ static void MessageCallback(cwiid_wiimote_t *wiiremote, int mesgCount, union cwiid_mesg mesg[]);
+#endif
+
+#ifndef _DEBUG
+/* This takes the errors generated at pre-connect and silence them as they are mostly not needed */
+ static void ErrorCallback(struct wiimote *wiiremote, const char *str, va_list ap);
+#endif
+
+ int m_Socket;
+ CAddress m_MyAddr;
+
+ // Mouse
+ bool m_haveIRSources;
+ bool m_isActive;
+ bool m_useIRMouse;
+ int m_lastActiveTime;
+
+/* The protected functions is for the static callbacks */
+ protected:
+ //Connection
+ void DisconnectNow(bool startConnectThread);
+
+ //Mouse
+ void CalculateMousePointer(int x1, int y1, int x2, int y2);
+// void SetIR(bool set);
+
+ //Button
+ void ProcessKey(int Key);
+
+ //Nunchuck
+ void ProcessNunchuck(struct cwiid_nunchuk_mesg &Nunchuck);
+#ifdef CWIID_OLD
+ //Disconnect check
+ void CheckIn();
+#endif
+};
+
+#endif // WII_REMOTE_H
+
+extern CWiiRemote g_WiiRemote;
diff --git a/tools/EventClients/Clients/WiiRemote/Makefile b/tools/EventClients/Clients/WiiRemote/Makefile
new file mode 100644
index 0000000000..4ceaa68bbb
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/Makefile
@@ -0,0 +1,13 @@
+CFLAGS = -Wall -pipe -fPIC -funroll-loops
+OBJ_DIR = release-$(shell $(CC) -v 2>&1|grep ^Target:|cut -d' ' -f2)
+OBJS = wiiuse_v0.12/src/$(OBJ_DIR)/libwiiuse.so
+BIN = WiiUse_WiiRemote
+VERSION = v0.12
+
+all:
+ @$(MAKE) -C wiiuse_$(VERSION)/src $@
+ g++ $(CFLAGS) WiiUse_WiiRemote.cpp $(OBJS) -o $(BIN)
+wiiuse:
+ @$(MAKE) -C wiiuse_$(VERSION)/src
+clean:
+ rm $(OBJS) $(BIN)
diff --git a/tools/EventClients/Clients/WiiRemote/WiiUse_README b/tools/EventClients/Clients/WiiRemote/WiiUse_README
new file mode 100644
index 0000000000..df67c2d7cf
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/WiiUse_README
@@ -0,0 +1,18 @@
+There are two ways to make the WiiUse_WiiRemote event client.
+
+1) Compile manually. Requires the wiiuse library to be installed already (http://sourceforge.net/project/showfiles.php?group_id=187194)
+g++ -lwiiuse WiiUse_WiiRemote.cpp -o WiiUse_WiiRemote
+
+2) Use the Makefile. Will make the wiiuse_v0.12 library from source. Executable must be run from this directory due to relative linking to the built library.
+make
+
+Both methods will create a WiiUse_WiiRemote executable containing the event client.
+
+
+The WiiUse_WiiRemote.h file contains the button number mappings for the Wii Remote (prefixed with KEYCODE).
+
+
+TODO:
+Add IR pointer support
+
+TheUni
diff --git a/tools/EventClients/Clients/WiiRemote/WiiUse_WiiRemote.cpp b/tools/EventClients/Clients/WiiRemote/WiiUse_WiiRemote.cpp
new file mode 100644
index 0000000000..bae0cf6f49
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/WiiUse_WiiRemote.cpp
@@ -0,0 +1,264 @@
+ /* Copyright (C) 2009 by Cory Fields
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+#include "WiiUse_WiiRemote.h"
+
+void CWiiController::get_keys(wiimote* wm)
+{
+ m_buttonHeld = 0;
+ m_buttonPressed = 0;
+ m_buttonReleased = 0;
+
+ m_repeatableHeld = (wm->btns & (m_repeatFlags));
+ m_holdableHeld = (wm->btns & (m_holdFlags));
+ m_repeatableReleased = (wm->btns_released & (m_repeatFlags));
+ m_holdableReleased = (wm->btns_released & (m_holdFlags));
+
+ for (int i = 1;i <= WIIMOTE_NUM_BUTTONS; i++)
+ {
+ if (IS_PRESSED(wm,convert_code(i)))
+ m_buttonPressed = i;
+ if (IS_RELEASED(wm,convert_code(i)))
+ m_buttonReleased = i;
+ if (IS_HELD(wm,convert_code(i)))
+ m_buttonHeld = i;
+ }
+}
+
+void CWiiController::handleKeyPress()
+{
+ if ((m_holdableReleased && m_buttonDownTime < g_hold_button_timeout))
+ EventClient.SendButton(m_buttonReleased, m_joyString, BTN_QUEUE | BTN_NO_REPEAT);
+ if (m_buttonPressed && !m_holdableHeld && !m_buttonHeld)
+ EventClient.SendButton(m_buttonPressed, m_joyString, BTN_QUEUE | BTN_NO_REPEAT);
+}
+
+void CWiiController::handleRepeat()
+{
+ if (m_repeatableHeld)
+ EventClient.SendButton(m_buttonPressed, m_joyString, BTN_QUEUE | BTN_NO_REPEAT, 5);
+}
+
+void CWiiController::handleACC(float currentRoll, float currentPitch)
+{
+ int rollWeight = 0;
+ int tiltWeight = 0;
+
+ if (m_start_roll == 0)
+ m_start_roll = currentRoll;
+ m_abs_roll = smoothDeg(m_abs_roll, currentRoll);
+ m_rel_roll = m_abs_roll - m_start_roll;
+ rollWeight = int((m_rel_roll*m_rel_roll));
+ if (rollWeight > 65000)
+ rollWeight = 65000;
+
+ if (m_start_pitch == 0)
+ m_start_pitch = currentPitch;
+ m_abs_pitch = smoothDeg(m_abs_pitch, currentPitch);
+ m_rel_pitch = m_start_pitch - m_abs_pitch;
+ tiltWeight = int((m_rel_pitch*m_rel_pitch*16));
+ if (tiltWeight > 65000)
+ tiltWeight = 65000;
+
+ if (m_currentAction == ACTION_NONE)
+ {
+ if ((g_deadzone - (abs((int)m_rel_roll)) < 5) && (abs((int)m_abs_pitch) < (g_deadzone / 1.5)))
+ // crossed the roll deadzone threshhold while inside the pitch deadzone
+ {
+ m_currentAction = ACTION_ROLL;
+ }
+ else if ((g_deadzone - (abs((int)m_rel_pitch)) < 5) && (abs((int)m_abs_roll) < (g_deadzone / 1.5)))
+ // crossed the pitch deadzone threshhold while inside the roll deadzone
+ {
+ m_currentAction = ACTION_PITCH;
+ }
+ }
+
+ if (m_currentAction == ACTION_ROLL)
+ {
+ if (m_rel_roll < -g_deadzone)
+ EventClient.SendButton(KEYCODE_ROLL_NEG, m_joyString, BTN_QUEUE | BTN_NO_REPEAT, rollWeight);
+ if (m_rel_roll > g_deadzone)
+ EventClient.SendButton(KEYCODE_ROLL_POS, m_joyString, BTN_QUEUE | BTN_NO_REPEAT, rollWeight);
+// printf("Roll: %f\n",m_rel_roll);
+ }
+
+ if (m_currentAction == ACTION_PITCH)
+ {
+ if (m_rel_pitch > g_deadzone)
+ EventClient.SendButton(KEYCODE_PITCH_POS, m_joyString, BTN_QUEUE | BTN_NO_REPEAT,tiltWeight);
+ if (m_rel_pitch < -g_deadzone)
+ EventClient.SendButton(KEYCODE_PITCH_NEG, m_joyString, BTN_QUEUE | BTN_NO_REPEAT,tiltWeight);
+// printf("Pitch: %f\n",m_rel_pitch);
+ }
+}
+
+void CWiiController::handleIR()
+{
+//TODO
+}
+
+int connectWiimote(wiimote** wiimotes)
+{
+ int found = 0;
+ int connected = 0;
+ wiimote* wm;
+ while(!found)
+ {
+ //Look for Wiimotes. Keep searching until one is found. Wiiuse provides no way to search forever, so search for 5 secs and repeat.
+ found = wiiuse_find(wiimotes, MAX_WIIMOTES, 5);
+ }
+ connected = wiiuse_connect(wiimotes, MAX_WIIMOTES);
+ wm = wiimotes[0];
+ if (connected)
+ {
+ EventClient.SendHELO("Wii Remote", ICON_PNG, NULL);
+ wiiuse_set_leds(wm, WIIMOTE_LED_1);
+ wiiuse_rumble(wm, 1);
+ wiiuse_set_orient_threshold(wm,1);
+ #ifndef WIN32
+ usleep(200000);
+ #else
+ Sleep(200);
+ #endif
+ wiiuse_rumble(wm, 0);
+
+ return 1;
+ }
+ return 0;
+}
+
+int handle_disconnect(wiimote* wm)
+{
+ EventClient.SendNOTIFICATION("Wii Remote disconnected", "", ICON_PNG, NULL);
+ return 0;
+}
+
+unsigned short convert_code(unsigned short client_code)
+{
+//WIIMOTE variables are from wiiuse. We need them to be 1-11 for the eventclient.
+//For that we use this ugly conversion.
+
+ switch (client_code)
+ {
+ case KEYCODE_BUTTON_UP: return WIIMOTE_BUTTON_UP;
+ case KEYCODE_BUTTON_DOWN: return WIIMOTE_BUTTON_DOWN;
+ case KEYCODE_BUTTON_LEFT: return WIIMOTE_BUTTON_LEFT;
+ case KEYCODE_BUTTON_RIGHT: return WIIMOTE_BUTTON_RIGHT;
+ case KEYCODE_BUTTON_A: return WIIMOTE_BUTTON_A;
+ case KEYCODE_BUTTON_B: return WIIMOTE_BUTTON_B;
+ case KEYCODE_BUTTON_MINUS: return WIIMOTE_BUTTON_MINUS;
+ case KEYCODE_BUTTON_HOME: return WIIMOTE_BUTTON_HOME;
+ case KEYCODE_BUTTON_PLUS: return WIIMOTE_BUTTON_PLUS;
+ case KEYCODE_BUTTON_ONE: return WIIMOTE_BUTTON_ONE;
+ case KEYCODE_BUTTON_TWO: return WIIMOTE_BUTTON_TWO;
+ default : break;
+ }
+return 0;
+}
+
+float smoothDeg(float oldVal, float newVal)
+{
+//If values go over 180 or under -180, weird things happen. This tranforms the values to -360 to 360 instead.
+
+ if (newVal - oldVal > 300)
+ return (newVal - 360);
+ if (oldVal - newVal > 300)
+ return(newVal + 360);
+ return(newVal);
+}
+
+long getTicks(void)
+{
+ long ticks;
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ ticks = now.tv_sec * 1000l;
+ ticks += now.tv_usec / 1000l;
+ return ticks;
+}
+
+int main(int argc, char** argv)
+{
+ long timeout = 0;
+ bool connected = 0;
+ wiimote** wiimotes;
+ wiimotes = wiiuse_init(MAX_WIIMOTES);
+ CWiiController controller;
+ wiimote* wm;
+ {
+ //Main Loop
+ while (1)
+ {
+ if (!connected) connected = (connectWiimote(wiimotes));
+ wm = wiimotes[0]; // Only worry about 1 controller. No need for more?
+ controller.m_buttonDownTime = getTicks() - timeout;
+
+//Handle ACC, Repeat, and IR outside of the Event loop so that buttons can be continuously sent
+ if (timeout)
+ {
+ if ((controller.m_buttonDownTime > g_hold_button_timeout) && controller.m_holdableHeld)
+ controller.handleACC(wm->orient.roll, wm->orient.pitch);
+ if ((controller.m_buttonDownTime > g_repeat_rate) && controller.m_repeatableHeld)
+ controller.handleRepeat();
+ }
+ if (wiiuse_poll(wiimotes, MAX_WIIMOTES))
+ {
+ for (int i = 0; i < MAX_WIIMOTES; ++i)
+ //MAX_WIIMOTES hardcoded at 1.
+ {
+ switch (wiimotes[i]->event)
+ {
+ case WIIUSE_EVENT:
+
+ controller.get_keys(wm); //Load up the CWiiController
+ controller.handleKeyPress();
+ if (!controller.m_buttonHeld && (controller.m_holdableHeld || controller.m_repeatableHeld))
+ {
+ //Prepare to repeat or hold. Do this only once.
+ timeout = getTicks();
+ EnableMotionSensing(wm);
+ controller.m_abs_roll = 0;
+ controller.m_abs_pitch = 0;
+ controller.m_start_roll = 0;
+ controller.m_start_pitch = 0;
+ }
+ if (controller.m_buttonReleased)
+ {
+ DisableMotionSensing(wm);
+ controller.m_currentAction = ACTION_NONE;
+ }
+ break;
+ case WIIUSE_STATUS:
+ break;
+ case WIIUSE_DISCONNECT:
+ case WIIUSE_UNEXPECTED_DISCONNECT:
+ handle_disconnect(wm);
+ connected = 0;
+ break;
+ case WIIUSE_READ_DATA:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+ wiiuse_cleanup(wiimotes, MAX_WIIMOTES);
+ return 0;
+}
diff --git a/tools/EventClients/Clients/WiiRemote/WiiUse_WiiRemote.h b/tools/EventClients/Clients/WiiRemote/WiiUse_WiiRemote.h
new file mode 100644
index 0000000000..b4eb749b9e
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/WiiUse_WiiRemote.h
@@ -0,0 +1,124 @@
+ /* Copyright (C) 2009 by Cory Fields
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+//#include <math.h>
+#ifdef DEB_PACK
+#include <xbmc/xbmcclient.h>
+#else
+#include "../../lib/c++/xbmcclient.h"
+#endif
+//#ifndef WIN32
+// #include <unistd.h>
+//#endif
+#include "wiiuse.h"
+//#define ICON_PATH "../../"
+#define PORT 9777
+#define MAX_WIIMOTES 1
+#define OVER_180_DEG 1
+#define UNDER_NEG_180_DEG 2
+#define WIIMOTE_BUTTON_TWO 0x0001
+#define WIIMOTE_BUTTON_ONE 0x0002
+#define WIIMOTE_BUTTON_B 0x0004
+#define WIIMOTE_BUTTON_A 0x0008
+#define WIIMOTE_BUTTON_MINUS 0x0010
+#define WIIMOTE_BUTTON_HOME 0x0080
+#define WIIMOTE_BUTTON_LEFT 0x0100
+#define WIIMOTE_BUTTON_RIGHT 0x0200
+#define WIIMOTE_BUTTON_DOWN 0x0400
+#define WIIMOTE_BUTTON_UP 0x0800
+#define WIIMOTE_BUTTON_PLUS 0x1000
+
+#define WIIMOTE_NUM_BUTTONS 11
+#define KEYCODE_BUTTON_UP 1
+#define KEYCODE_BUTTON_DOWN 2
+#define KEYCODE_BUTTON_LEFT 3
+#define KEYCODE_BUTTON_RIGHT 4
+#define KEYCODE_BUTTON_A 5
+#define KEYCODE_BUTTON_B 6
+#define KEYCODE_BUTTON_MINUS 7
+#define KEYCODE_BUTTON_HOME 8
+#define KEYCODE_BUTTON_PLUS 9
+#define KEYCODE_BUTTON_ONE 10
+#define KEYCODE_BUTTON_TWO 11
+#define KEYCODE_ROLL_NEG 33
+#define KEYCODE_ROLL_POS 34
+#define KEYCODE_PITCH_NEG 35
+#define KEYCODE_PITCH_POS 36
+
+
+#define ACTION_NONE 0
+#define ACTION_ROLL 1
+#define ACTION_PITCH 2
+
+class CWiiController{
+ public:
+
+ bool m_holdableHeld;
+ bool m_holdableReleased;
+ bool m_repeatableHeld;
+ bool m_repeatableReleased;
+
+ unsigned short m_buttonPressed;
+ unsigned short m_buttonReleased;
+ unsigned short m_buttonHeld;
+ unsigned short m_repeatFlags;
+ unsigned short m_holdFlags;
+ unsigned short m_currentAction;
+
+ long m_buttonDownTime;
+
+ float m_abs_roll;
+ float m_abs_pitch;
+ float m_rel_roll;
+ float m_rel_pitch;
+ float m_start_roll;
+ float m_start_pitch;
+ char* m_joyString;
+
+
+ void get_keys(wiimote* wm);
+ void handleKeyPress();
+ void handleRepeat();
+ void handleACC(float, float);
+ void handleIR();
+
+ CWiiController()
+ {
+ m_joyString = "JS0:WiiRemote";
+ m_repeatFlags = WIIMOTE_BUTTON_UP | WIIMOTE_BUTTON_DOWN | WIIMOTE_BUTTON_LEFT | WIIMOTE_BUTTON_RIGHT;
+ m_holdFlags = WIIMOTE_BUTTON_B;
+ }
+};
+
+unsigned short g_deadzone = 30;
+unsigned short g_hold_button_timeout = 200;
+unsigned short g_repeat_rate = 400;
+
+int handle_disconnect(wiimote* );
+int connectWiimote(wiimote** );
+long getTicks(void);
+void EnableMotionSensing(wiimote* wm) {if (!WIIUSE_USING_ACC(wm)) wiiuse_motion_sensing(wm, 1);}
+void DisableMotionSensing(wiimote* wm) {if (WIIUSE_USING_ACC(wm)) wiiuse_motion_sensing(wm, 0);}
+unsigned short convert_code(unsigned short);
+float smoothDeg(float, float);
+
+CXBMCClient EventClient;
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/CHANGELOG b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/CHANGELOG
new file mode 100644
index 0000000000..ccb831af97
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/CHANGELOG
@@ -0,0 +1,208 @@
+---------------------------
+-
+- CHANGE LOG - Wiiuse
+-
+---------------------------
+
+http://wiiuse.net/
+http://wiiuse.sourceforge.net/
+http://sourceforge.net/projects/wiiuse/
+
+---------------------------
+v0.12 -- 2 Apr 2008
+---------------------------
+
+ Added:
+ - API function wiiuse_set_ir_sensitivity()
+ - Macro WIIUSE_GET_IR_SENSITIVITY()
+ - Event type WIIUSE_READ_DATA
+ - Event type WIIUSE_UNEXPECTED_DISCONNECT
+
+ Fixed:
+ - [Linux] Ability to try to select() nothing
+ - [Linux] Changed Makefile to include debug output when compiling in debug mode
+
+ Changed:
+ - wiiuse_set_nunchuk_orient_threshold() now takes a wiimote_t pointer
+ - wiiuse_set_nunchuk_accel_threshold() now takes a wiimote_t pointer
+ - wiiuse_read_data() generates an event WIIUSE_READ_DATA rather than executing a callback
+
+---------------------------
+v0.11 -- 25 Feb 2008
+---------------------------
+
+ Added:
+ - API function wiiuse_set_nunchuk_orient_threshold()
+ - API function wiiuse_set_nunchuk_accel_threshold()
+ - Event type WIIUSE_NUNCHUK_INSERTED
+ - Event type WIIUSE_NUNCHUK_REMOVED
+ - Event type WIIUSE_CLASSIC_CTRL_INSERTED
+ - Event type WIIUSE_CLASSIC_CTRL_REMOVED
+ - Event type WIIUSE_GUITAR_HERO_3_CTRL_INSERTED
+ - Event type WIIUSE_GUITAR_HERO_3_CTRL_REMOVED
+
+ Fixed:
+ - Added some missing function prototypes to wiiuse.h
+ - [Linux] Fixed Makefile to link libmath and libbluetooth
+ - Status event is set when a status report comes in
+ - Orientation threshold not being saved in lstate
+
+---------------------------
+v0.10 -- 11 Feb 2008
+---------------------------
+
+ Added:
+ - Real dynamic linking (by noisehole)
+ - Changed from callback to SDL style
+ - Guitar Hero 3 controller support
+ - API function wiiuse_set_accel_threshold()
+ - API function wiiuse_version()
+ - Macro WIIUSE_USING_SPEAKER()
+ - Macro WIIUSE_IS_LED_SET(wm, num)
+ - wiiuse_init() now autogenerates unids
+ - orient_t::a_roll/a_pitch
+ - wiiuse_resync()
+ - wiiuse_cleanup()
+ - wiiuse_set_timeout()
+
+ Fixed:
+ - [Windows] Fixed bug where it did not detect expansions on startup
+ - Renamed INFO/WARNING/DEBUG macros to WIIUSE_* (by noisehole)
+ - Updated Makefiles (by noisehole)
+ - Fixed incorrect roll/pitch when smoothing was enabled
+ - Fixed nunchuk and classic controller flooding events when significant changes occured
+ - Fixed bug where IR was not correct on roll if IR was enabled before handshake
+
+ Removed:
+ - wiiuse_startup(), no longer needed
+
+---------------------------
+v0.9 -- 3 Nov 2007
+---------------------------
+
+ Fixed:
+ - Can now use include/wiiuse.h in C++ projects.
+ - HOME button works again.
+ - IR now functions after expansion is connected or removed.
+
+---------------------------
+v0.8 -- 27 Oct 2007
+---------------------------
+
+ - Bumped API version to 8
+ - Exported all API functions for usage with non-C/C++ languages.
+ - Changed event callback to only trigger if a significant state change occurs.
+
+ Added:
+ - wiimote_t::lstate structure
+
+ Fixed:
+ - Bug 1820140 - Buffer overflow in io_nix.c. Thanks proppy.
+
+---------------------------
+v0.7 -- 19 Oct 2007
+---------------------------
+
+ - Bumped API version to 7
+ - Renamed Linux build from wii.so to wiiuse.so
+ - Changed version representation from float to const char*.
+
+ Added:
+ - [Windows] BlueSoleil support.
+ - [Windows] Bluetooth stack auto-detection (WinXP SP2, Bluesoleil, Widdcomm tested).
+ - [Windows] API function wiiuse_set_bluetooth_stack().
+ - Calculates yaw if IR tracking is enabled.
+
+ Fixed:
+ - [Windows] Problem where a connection is made to a wiimote that does not exist.
+ - [Windows] Issue that occured while using multiple wiimotes.
+
+---------------------------
+v0.6 -- 16 Oct 2007
+---------------------------
+
+ - Bumped API version to 0.6.
+ - Ported to Microsoft Windows.
+ - Improved IR tracking.
+ - Default IR virtual screen resolutions changed depending on 16:9 or 4:3.
+
+ Added:
+ - src/msvc/ and api/msvc/ - Microsoft Visual C++ 6.0 project files.
+
+---------------------------
+v0.5 -- 13 Oct 2007
+---------------------------
+
+ - Bumped API version to 0.5.
+ - Greatly improved IR tracking.
+ - Renamed function wiiuse_set_ir_correction() to wiiuse_set_ir_position().
+
+ Added:
+ - API function wiiuse_set_aspect_ratio()
+
+ Fixed:
+ - When rolling around 180 degree rotation smoothing would not be seemless.
+
+---------------------------
+v0.4 -- 08 Oct 2007
+---------------------------
+
+ - Bumped API version to 0.4.
+ - Greatly improved classic controller joystick functionality.
+ - Changed all functions named wiimote_*() to wiiuse_*()
+ - Renamed many macros from WIIMOTE_* to WIIUSE_*
+
+ Added:
+ - IR support
+ - New WIIMOTE_CONTINUOUS flag to set continuous reporting
+ - Macro IS_JUST_PRESSED()
+ - Macro WIIUSE_USING_ACC()
+ - Macro WIIUSE_USING_EXP()
+ - Macro WIIUSE_USING_IR()
+ - API function wiiuse_set_ir()
+ - API function wiiuse_set_ir_vres()
+ - API function wiiuse_set_ir_correction()
+
+ - gfx/ - A small OpenGL example that renders IR data
+
+ Fixed:
+ - Sometimes classic controller would only report infinite angle and magnitude for joysticks.
+
+---------------------------
+v0.3 -- 10 Sept 2007
+---------------------------
+
+ - Moved license to GPLv3.
+ - Bumped API version to 0.3.
+
+ Added:
+ - Support for Classic Controller
+ - Smoothing for roll and pitch values of the wiimote and nunchuk.
+
+ - API: wiimote_set_flags() to set or disable wiimote options.
+ - API: wiimote_set_smooth_alpha() to set smoothing alpha value.
+
+ Fixed:
+ - When the wiimote accelerates the roll or pitch is unreliable and was set to 0.
+ It now remains at previous tilt value.
+ - If no event callback was specified then no events would be processed internally.
+
+---------------------------
+v0.2 -- 25 Aug 2007
+---------------------------
+
+ - Bumped API version to 0.2.
+
+ Added:
+ - Nunchuk support.
+ - Ability to write to flash memory.
+
+ Fixed:
+ - Roll and pitch rotation now ranges from -180 to 180 degrees (previously -90 to 90).
+ - Bug when reading data from flash memory would read wrong address.
+
+---------------------------
+v0.1 -- 23 Feb 2007
+---------------------------
+
+ - Initial release
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/LICENSE b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/LICENSE
new file mode 100644
index 0000000000..85b8e59922
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/LICENSE
@@ -0,0 +1,620 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/LICENSE_noncommercial b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/LICENSE_noncommercial
new file mode 100644
index 0000000000..b556a2278c
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/LICENSE_noncommercial
@@ -0,0 +1,170 @@
+This license can be used for any works that this license defines
+as "derived" or "combined" works that are strictly NON-COMMERCIAL.
+This license prohibits the use of any source or object code
+in works that are PROPRIETARY.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/Makefile b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/Makefile
new file mode 100644
index 0000000000..f63da694f8
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/Makefile
@@ -0,0 +1,23 @@
+#
+# wiiuse Makefile
+#
+
+all clean install:
+ @$(MAKE) -C src $@
+ @$(MAKE) -C example $@
+ @$(MAKE) -C example-sdl $@
+
+wiiuse:
+ @$(MAKE) -C src
+
+ex:
+ @$(MAKE) -C example
+
+sdl-ex:
+ @$(MAKE) -C example-sdl
+
+distclean:
+ @$(MAKE) -C src $@
+ @$(MAKE) -C example $@
+ @$(MAKE) -C example-sdl $@
+ @find . -type f \( -name "*.save" -or -name "*~" -or -name gmon.out \) -delete &> /dev/null
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/README b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/README
new file mode 100644
index 0000000000..12d9f71517
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/README
@@ -0,0 +1,155 @@
+---------------------------
+-
+- README - Wiiuse
+-
+---------------------------
+
+http://wiiuse.net/
+http://wiiuse.sourceforge.net/
+http://sourceforge.net/projects/wiiuse/
+
+---------------------------
+
+ABOUT
+
+ Wiiuse is a library written in C that connects with several Nintendo Wii remotes.
+ Supports motion sensing, IR tracking, nunchuk, classic controller, and the Guitar Hero 3 controller.
+ Single threaded and nonblocking makes a light weight and clean API.
+
+ Distributed under the GPL and LGPL.
+
+
+AUTHORS
+
+ Michael Laforest < para >
+ Email: < thepara (--AT--) g m a i l [--DOT--] com >
+
+ The following people have contributed patches to wiiuse:
+ - dhewg
+
+
+LICENSE
+
+ There are two licenses for wiiuse. Please read them carefully before choosing which one
+ you use. You may of course at any time switch the license you are currently using to
+ the other.
+
+ Briefly, the license options are:
+
+ a) GNU LGPL (modified for non-commercial usage only)
+ b) GNU GPL
+
+ PLEASE READ THE LICENSES!
+
+
+ACKNOWLEDGEMENTS
+
+ http://wiibrew.org/
+ This site and their users have contributed an immense amount of information
+ about the wiimote and its technical details. I could not have written this program
+ without the vast amounts of reverse engineered information that was researched by them.
+
+ Nintendo
+ Of course Nintendo for designing and manufacturing the Wii and Wii remote.
+
+ BlueZ
+ Easy and intuitive bluetooth stack for Linux.
+
+ Thanks to Brent for letting me borrow his Guitar Hero 3 controller.
+
+
+DISCLAIMER AND WARNINGS
+
+ I am in no way responsible for any damages or effects, intended or not, caused by this program.
+
+ *** WARNING *** WARNING *** WARNING ***
+
+ Be aware that writing to memory may damage or destroy your wiimote or expansions.
+
+ *** WARNING *** WARNING *** WARNING ***
+
+ This program was written using reverse engineered specifications available from wiibrew.org.
+ Therefore the operation of this program may not be entirely correct.
+ Results obtained by using this program may vary.
+
+
+AUDIENCE
+
+ This project is intended for developers who wish to include support for the Nintendo Wii remote
+ with their third party application. Please be aware that by using this software you are bound
+ to the terms of the GNU GPL.
+
+
+PLATFORMS AND DEPENDENCIES
+
+ Wiiuse currently operates on both Linux and Windows.
+ You will need:
+
+ For Linux:
+ - The kernel must support bluetooth
+ - The BlueZ bluetooth drivers must be installed
+
+ For Windows:
+ - Bluetooth driver (tested with Microsoft's stack with Windows XP SP2)
+ - If compiling, Microsoft Windows Driver Development Kit (DDK)
+
+
+COMPILING
+
+ Linux:
+ You need SDL and OpenGL installed to compile the SDL example.
+
+ # make [target]
+
+ If 'target' is omitted then everything is compiled.
+
+ Where 'target' can be any of the following:
+
+ - wiiuse
+ Compiles libwiiuse.so
+
+ - ex
+ Compiles wiiuse-example
+
+ - sdl-ex
+ Compiles wiiuse-sdl
+
+ Become root.
+
+ # make install
+
+ The above command will only install the binaries that you
+ selected to compile.
+
+ wiiuse.so is installed to /usr/lib
+ wiiuse-example and wiiuse-sdl are installed to /usr/bin
+
+ Windows:
+ A Microsoft Visual C++ 6.0 project file has been included.
+
+ You need the install the Windows DDK (driver development kit)
+ to compile wiiuse. Make sure you include the inc/ and lib/
+ directories from the DDK in your IDE include and library paths.
+ You can download this from here:
+ http://www.microsoft.com/whdc/devtools/ddk/default.mspx
+
+ You need to link the following libraries (already set in the
+ included project file):
+ - Ws2_32.lib
+ - hid.lib
+ - setupapi.lib
+
+
+USING THE LIBRARY IN A THIRD PARTY APPLICATION
+
+ To use the library in your own program you must first compile wiiuse as a module.
+ Include include/wiiuse.h in any file that uses wiiuse.
+
+ For Linux you must link wiiuse.so ( -lwiiuse ).
+ For Windows you must link wiiuse.lib. When your program runs it will need wiiuse.dll.
+
+
+BUGS
+
+ On Windows using more than one wiimote (usually more than two wiimotes) may cause
+ significant latency.
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/Makefile b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/Makefile
new file mode 100644
index 0000000000..c206354141
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/Makefile
@@ -0,0 +1,83 @@
+#
+# wiiuse Makefile
+#
+
+#
+# Change this to your GCC version.
+#
+CC = gcc
+
+####################################################
+#
+# You should not need to edit below this line.
+#
+####################################################
+
+#
+# Universal cflags
+#
+CFLAGS = -Wall -pipe -fPIC -funroll-loops
+
+ifeq ($(debug),1)
+ OBJ_PREFIX = debug
+ CFLAGS += -g -pg -DWITH_WIIUSE_DEBUG
+else
+ OBJ_PREFIX = release
+ CFLAGS += -O2
+endif
+
+OBJ_DIR = $(OBJ_PREFIX)-$(shell $(CC) -v 2>&1|grep ^Target:|cut -d' ' -f2)
+
+#
+# Linking flags
+#
+LDFLAGS = -L../src/$(OBJ_DIR) -lm -lGL -lGLU -lglut -lSDL -lbluetooth -lwiiuse
+
+#
+# Target binaries (always created as BIN)
+#
+BIN = ./$(OBJ_DIR)/wiiuse-sdl
+
+#
+# Inclusion paths.
+#
+INCLUDES = -I../src/ -I/usr/include/SDL
+
+#
+# Generate a list of object files
+#
+OBJS = $(OBJ_DIR)/sdl.o
+
+###############################
+#
+# Build targets.
+#
+###############################
+
+all: $(BIN)
+
+clean:
+ @-rm $(OBJS) 2> /dev/null
+
+distclean: clean
+ @-rm -r debug-* release-* 2> /dev/null
+
+install:
+ @if [ -e $(BIN) ]; then \
+ cp -v $(BIN) /usr/bin ;\
+ fi
+
+$(BIN): mkdir $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(BIN)
+
+$(OBJ_DIR)/%.o: %.c
+ $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+
+mkdir:
+ @if [ ! -d $(OBJ_DIR) ]; then \
+ mkdir $(OBJ_DIR); \
+ fi
+
+run: all
+ LD_LIBRARY_PATH=`pwd`/../src/$(OBJ_DIR):$(LD_LIBRARY_PATH) $(BIN)
+
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.dsp b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.dsp
new file mode 100644
index 0000000000..23c4a24c2a
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.dsp
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="wiiusesdl" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=wiiusesdl - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "wiiusesdl.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "wiiusesdl.mak" CFG="wiiusesdl - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "wiiusesdl - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "wiiusesdl - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "wiiusesdl - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\src" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl.lib opengl32.lib glu32.lib wiiuse.lib /nologo /subsystem:windows /machine:I386
+
+!ELSEIF "$(CFG)" == "wiiusesdl - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\src" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib sdl.lib opengl32.lib glu32.lib wiiuse.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "wiiusesdl - Win32 Release"
+# Name "wiiusesdl - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\sdl.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\..\src\wiiuse.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.dsw b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.dsw
new file mode 100644
index 0000000000..630b942593
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "wiiusesdl"=".\wiiusesdl.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.opt b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.opt
new file mode 100644
index 0000000000..fa46687ca8
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/msvc/wiiusesdl.opt
Binary files differ
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/sdl.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/sdl.c
new file mode 100644
index 0000000000..0c0cdfb993
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example-sdl/sdl.c
@@ -0,0 +1,439 @@
+/*
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ */
+
+#include <stdlib.h>
+#include <math.h>
+
+#ifndef WIN32
+ #include <unistd.h>
+ #include <sys/time.h>
+ #include <time.h>
+#else
+ #include <windows.h>
+#endif
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+#include <GL/glut.h>
+#include <SDL.h>
+
+#include <wiiuse.h>
+
+#define PI 3.14159265358979323846
+#define PI_DIV_180 0.017453292519943296
+#define deg PI_DIV_180
+
+#define MAX_WIIMOTES 2
+
+GLint width = 1024, height = 768;
+GLfloat backColor[4] = {1.0,1.0,1.0,1.0};
+
+wiimote** wiimotes = NULL;
+
+int last_dots[4][2] = {{0}};
+int xcoord = 0;
+int ycoord = 0;
+
+#ifdef WIN32
+ DWORD last_render;
+#else
+ struct timeval last_render;
+ int last_sec = 0;
+ int fps = 0;
+#endif
+
+enum render_mode_t {
+ IR = 1,
+ TEAPOT
+};
+enum render_mode_t render_mode = IR;
+
+/* light information */
+struct light_t {
+ GLfloat position[4];
+ GLfloat spotDirection[3];
+ GLfloat ambient[4];
+ GLfloat diffuse[4];
+ GLfloat specular[4];
+ GLfloat spotCutoff;
+ GLfloat spotExponent;
+ GLfloat spotAttenuation[3]; /* [0] = constant, [1] = linear, [2] = quadratic */
+};
+struct light_t light = {
+ { 1.0, 1.0, -2.0, 1.0 },
+ { -1.0, -1.0, 2.0 },
+ { 0.0, 0.0, 0.0, 1.0 },
+ { 1.0, 1.0, 1.0, 1.0 },
+ { 1.0, 1.0, 1.0, 1.0 },
+ 180.0, 0.0,
+ { 1.0, 0.0, 0.0 }
+};
+
+/* material information */
+struct material_t {
+ GLfloat ambient[4];
+ GLfloat diffuse[4];
+ GLfloat specular[4];
+ GLfloat emission[4];
+ GLfloat shininess;
+};
+struct material_t red_plastic = {
+ { 0.3, 0.0, 0.0, 1.0 },
+ { 0.3, 0.0, 0.0, 1.0 },
+ { 0.8, 0.6, 0.6, 1.0 },
+ { 0.0, 0.0, 0.0, 1.0 },
+ 32.0
+};
+
+
+void handle_event(struct wiimote_t* wm);
+void display();
+void update_light(GLenum l, struct light_t* lptr);
+void set_material(struct material_t* mptr);
+void resize_window(GLint new_width, GLint new_height);
+
+void handle_event(struct wiimote_t* wm) {
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_PLUS))
+ wiiuse_motion_sensing(wm, 1);
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_MINUS))
+ wiiuse_motion_sensing(wm, 0);
+
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_UP))
+ wiiuse_set_ir(wm, 1);
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_DOWN))
+ wiiuse_set_ir(wm, 0);
+
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_B))
+ wiiuse_toggle_rumble(wm);
+
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_ONE)) {
+ int level;
+ WIIUSE_GET_IR_SENSITIVITY(wm, &level);
+ wiiuse_set_ir_sensitivity(wm, level+1);
+ }
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_TWO)) {
+ int level;
+ WIIUSE_GET_IR_SENSITIVITY(wm, &level);
+ wiiuse_set_ir_sensitivity(wm, level-1);
+ }
+
+ #if 0
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_A)) {
+ if (render_mode == IR)
+ render_mode = TEAPOT;
+ else
+ render_mode = IR;
+ resize_window(width, height);
+ }
+ #endif
+}
+
+#define DRAW_TRIANGLE(x, y, z, s) do { \
+ glVertex3f(x, y-s, z); \
+ glVertex3f(x+s, y+s, z); \
+ glVertex3f(x-s, y+s, z); \
+ } while (0)
+
+int can_render() {
+ /* quick fps limit to ~60fps -- not too fancy, could be better */
+ #ifdef WIN32
+ if (GetTickCount() < (last_render + 16))
+ return 0;
+ last_render = GetTickCount();
+ return 1;
+ #else
+ struct timeval now;
+ long elapsed_usec = 0;
+
+ gettimeofday(&now, NULL);
+
+ if (now.tv_usec > 1000000) {
+ now.tv_usec -= 1000000;
+ ++now.tv_sec;
+ }
+
+ if (now.tv_sec > last_render.tv_sec)
+ elapsed_usec = ((now.tv_sec - last_render.tv_sec) * 1000000);
+
+ if (now.tv_usec > last_render.tv_usec)
+ elapsed_usec += now.tv_usec - last_render.tv_usec;
+ else
+ elapsed_usec += last_render.tv_usec - now.tv_usec;
+
+ if (time(NULL) > last_sec) {
+ printf("fps: %i\n", fps);
+ fps = 0;
+ last_sec = time(NULL);
+ }
+
+ if (elapsed_usec < 16000)
+ return 0;
+
+ last_render = now;
+ ++fps;
+
+ return 1;
+ #endif
+}
+
+void display() {
+ int i, wm;
+ float size = 5;
+
+ if (!can_render())
+ return;
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ if (render_mode == IR) {
+ /* draw the IR stuff */
+
+ glDisable(GL_LIGHTING);
+
+ glBegin(GL_TRIANGLES);
+ /* green center */
+ glColor3f(0.0, 1.0, 0.0);
+ DRAW_TRIANGLE(width/2, height/2, 0, size);
+ glEnd();
+
+ for (wm = 0; wm < MAX_WIIMOTES; ++wm) {
+ glBegin(GL_TRIANGLES);
+ /* red ir */
+ glColor3f(1.0, 0.0, 0.0);
+ for (i = 0; i < 4; ++i) {
+ if (wiimotes[wm]->ir.dot[i].visible)
+ DRAW_TRIANGLE(wiimotes[wm]->ir.dot[i].rx, wiimotes[wm]->ir.dot[i].ry, 0, size);
+ }
+
+ /* yellow corrected ir */
+ glColor3f(1.0, 1.0, 0.0);
+ for (i = 0; i < 4; ++i) {
+ if (wiimotes[wm]->ir.dot[i].visible)
+ DRAW_TRIANGLE(wiimotes[wm]->ir.dot[i].x, wiimotes[wm]->ir.dot[i].y, 0, size);
+ }
+
+ /* blue cursor */
+ glColor3f(0.0, 0.0, 1.0);
+ DRAW_TRIANGLE(wiimotes[wm]->ir.x, wiimotes[wm]->ir.y-size, 0, size);
+ glEnd();
+ }
+ } else {
+ /* draw the teapot */
+ gluLookAt(0.0, 0.0, -5.0,
+ 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0);
+
+ glEnable(GL_LIGHTING);
+ glEnable(GL_LIGHT0);
+ update_light(GL_LIGHT0, &light);
+ set_material(&red_plastic);
+
+ glRotatef(wiimotes[0]->orient.roll, 0.0f, 0.0f, 1.0f);
+ glRotatef(wiimotes[0]->orient.pitch, 1.0f, 0.0f, 0.0f);
+
+
+ glutSolidTeapot(1);
+ }
+
+ SDL_GL_SwapBuffers();
+}
+
+
+void update_light(GLenum l, struct light_t* lptr) {
+ glLightfv(l, GL_POSITION, lptr->position);
+ glLightfv(l, GL_DIFFUSE, lptr->diffuse);
+ glLightfv(l, GL_SPECULAR, lptr->specular);
+ glLightfv(l, GL_AMBIENT, lptr->ambient);
+ glLightfv(l, GL_SPOT_DIRECTION, lptr->spotDirection);
+ glLightf(l, GL_SPOT_CUTOFF, lptr->spotCutoff);
+ glLightf(l, GL_SPOT_EXPONENT, lptr->spotExponent);
+ glLightf(l, GL_CONSTANT_ATTENUATION, lptr->spotAttenuation[0]);
+ glLightf(l, GL_LINEAR_ATTENUATION, lptr->spotAttenuation[1]);
+ glLightf(l, GL_QUADRATIC_ATTENUATION, lptr->spotAttenuation[2]);
+}
+
+
+void set_material(struct material_t* mptr) {
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mptr->ambient);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mptr->diffuse);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mptr->specular);
+ glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, mptr->shininess);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mptr->emission);
+}
+
+
+void resize_window(GLint new_width, GLint new_height) {
+ int wm;
+
+ width = new_width;
+ height = new_height;
+
+ if (new_height == 0)
+ new_height = 1;
+
+ SDL_SetVideoMode(width, height, 16, SDL_RESIZABLE | SDL_OPENGL);
+
+ glViewport(0, 0, new_width, new_height);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+
+ if (render_mode == IR)
+ gluOrtho2D(0, width, height, 0);
+ else
+ gluPerspective(60.0f, (float)new_width/(float)new_height, 0.1f, 100.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ width = new_width;
+ height = new_height;
+
+ for (wm = 0; wm < MAX_WIIMOTES; ++wm)
+ wiiuse_set_ir_vres(wiimotes[wm], width, height);
+}
+
+#ifndef WIN32
+int main(int argc, char** argv) {
+#else
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
+#endif
+ int found, connected;
+ int wm;
+
+ //printf("wiiuse version = %s\n", wiiuse_version());
+
+ wiimotes = wiiuse_init(MAX_WIIMOTES);
+ found = wiiuse_find(wiimotes, MAX_WIIMOTES, 5);
+ if (!found)
+ return 0;
+ connected = wiiuse_connect(wiimotes, MAX_WIIMOTES);
+ if (connected)
+ printf("Connected to %i wiimotes (of %i found).\n", connected, found);
+ else {
+ printf("Failed to connect to any wiimote.\n");
+ return 0;
+ }
+ wiiuse_set_leds(wiimotes[0], WIIMOTE_LED_1 | WIIMOTE_LED_4);
+ wiiuse_set_leds(wiimotes[1], WIIMOTE_LED_2 | WIIMOTE_LED_4);
+ wiiuse_rumble(wiimotes[0], 1);
+
+ #ifndef WIN32
+ usleep(200000);
+ #else
+ Sleep(200);
+ #endif
+
+ wiiuse_rumble(wiimotes[0], 0);
+
+ /* enable IR and motion sensing for all wiimotes */
+ for (wm = 0; wm < MAX_WIIMOTES; ++wm) {
+ wiiuse_motion_sensing(wiimotes[wm], 1);
+ wiiuse_set_ir(wiimotes[wm], 1);
+ }
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0) {
+ printf("Failed to initialize SDL: %s\n", SDL_GetError());
+ return 0;
+ }
+
+ SDL_WM_SetCaption("wiiuse SDL IR Example", "wiiuse SDL IR Example");
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
+
+ /* set window size */
+ width = wiimotes[0]->ir.vres[0];
+ height = wiimotes[0]->ir.vres[1];
+ SDL_SetVideoMode(width, height, 16, SDL_RESIZABLE | SDL_OPENGL);
+
+ for (wm = 0; wm < MAX_WIIMOTES; ++wm)
+ wiiuse_set_ir_vres(wiimotes[wm], width, height);
+
+ /* set OpenGL stuff */
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_LIGHTING);
+ glEnable(GL_NORMALIZE);
+ glEnable(GL_BLEND);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDepthFunc(GL_LEQUAL);
+ glClearColor(0, 0, 0, 0);
+
+ /* set the size of the window */
+ resize_window(width, height);
+
+ display();
+
+ #ifdef WIN32
+ last_render = GetTickCount();
+ #endif
+
+ while (1) {
+ SDL_Event event;
+
+ if (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_VIDEORESIZE:
+ {
+ /* resize the window */
+ resize_window(event.resize.w, event.resize.h);
+ break;
+ }
+ case SDL_QUIT:
+ {
+ /* shutdown */
+ SDL_Quit();
+ wiiuse_cleanup(wiimotes, MAX_WIIMOTES);
+ return 0;
+ }
+ default:
+ {
+ }
+ }
+ }
+
+ if (wiiuse_poll(wiimotes, MAX_WIIMOTES)) {
+ /*
+ * This happens if something happened on any wiimote.
+ * So go through each one and check if anything happened.
+ */
+ int i = 0;
+ for (; i < MAX_WIIMOTES; ++i) {
+ switch (wiimotes[i]->event) {
+ case WIIUSE_EVENT:
+ /* a generic event occured */
+ handle_event(wiimotes[i]);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ display();
+ }
+}
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/Makefile b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/Makefile
new file mode 100644
index 0000000000..41b5cd1e65
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/Makefile
@@ -0,0 +1,84 @@
+#
+# wiiuse Makefile
+#
+
+#
+# Change this to your GCC version.
+#
+CC = gcc
+
+####################################################
+#
+# You should not need to edit below this line.
+#
+####################################################
+
+#
+# Universal cflags
+#
+CFLAGS = -Wall -pipe -fPIC -funroll-loops
+
+ifeq ($(debug),1)
+ OBJ_PREFIX = debug
+ CFLAGS += -g -pg -DWITH_WIIUSE_DEBUG
+else
+ OBJ_PREFIX = release
+ CFLAGS += -O2
+endif
+
+OBJ_DIR = $(OBJ_PREFIX)-$(shell $(CC) -v 2>&1|grep ^Target:|cut -d' ' -f2)
+
+#
+# Linking flags
+#
+LDFLAGS = -L../src/$(OBJ_DIR) -lm -lwiiuse
+
+#
+# Target binaries (always created as BIN)
+#
+BIN = ./$(OBJ_DIR)/wiiuse-example
+
+#
+# Inclusion paths.
+#
+INCLUDES = -I../src/
+
+#
+# Generate a list of object files
+#
+OBJS = $(OBJ_DIR)/example.o
+
+###############################
+#
+# Build targets.
+#
+###############################
+
+all: $(BIN)
+
+clean:
+ @-rm $(OBJS) 2> /dev/null
+
+distclean:
+ @-rm -r debug-* release-* 2> /dev/null
+
+install:
+ @if [ -e $(BIN) ]; then \
+ cp -v $(BIN) /usr/bin ; \
+ fi
+
+
+$(BIN): mkdir $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(BIN)
+
+$(OBJ_DIR)/%.o: %.c
+ $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+
+mkdir:
+ @if [ ! -d $(OBJ_DIR) ]; then \
+ mkdir $(OBJ_DIR); \
+ fi
+
+run: all
+ LD_LIBRARY_PATH=`pwd`/../src/$(OBJ_DIR):$(LD_LIBRARY_PATH) $(BIN)
+
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/example.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/example.c
new file mode 100644
index 0000000000..8536cf683c
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/example.c
@@ -0,0 +1,437 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ *
+ * @brief Example using the wiiuse API.
+ *
+ * This file is an example of how to use the wiiuse library.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef WIN32
+ #include <unistd.h>
+#endif
+
+#include "wiiuse.h"
+
+
+#define MAX_WIIMOTES 4
+
+
+/**
+ * @brief Callback that handles an event.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ *
+ * This function is called automatically by the wiiuse library when an
+ * event occurs on the specified wiimote.
+ */
+void handle_event(struct wiimote_t* wm) {
+ printf("\n\n--- EVENT [id %i] ---\n", wm->unid);
+
+ /* if a button is pressed, report it */
+ if (IS_PRESSED(wm, WIIMOTE_BUTTON_A)) printf("A pressed\n");
+ if (IS_PRESSED(wm, WIIMOTE_BUTTON_B)) printf("B pressed\n");
+ if (IS_PRESSED(wm, WIIMOTE_BUTTON_UP)) printf("UP pressed\n");
+ if (IS_PRESSED(wm, WIIMOTE_BUTTON_DOWN)) printf("DOWN pressed\n");
+ if (IS_PRESSED(wm, WIIMOTE_BUTTON_LEFT)) printf("LEFT pressed\n");
+ if (IS_PRESSED(wm, WIIMOTE_BUTTON_RIGHT)) printf("RIGHT pressed\n");
+ if (IS_PRESSED(wm, WIIMOTE_BUTTON_MINUS)) printf("MINUS pressed\n");
+ if (IS_PRESSED(wm, WIIMOTE_BUTTON_PLUS)) printf("PLUS pressed\n");
+ if (IS_PRESSED(wm, WIIMOTE_BUTTON_ONE)) printf("ONE pressed\n");
+ if (IS_PRESSED(wm, WIIMOTE_BUTTON_TWO)) printf("TWO pressed\n");
+ if (IS_PRESSED(wm, WIIMOTE_BUTTON_HOME)) printf("HOME pressed\n");
+
+ /*
+ * Pressing minus will tell the wiimote we are no longer interested in movement.
+ * This is useful because it saves battery power.
+ */
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_MINUS))
+ wiiuse_motion_sensing(wm, 0);
+
+ /*
+ * Pressing plus will tell the wiimote we are interested in movement.
+ */
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_PLUS))
+ wiiuse_motion_sensing(wm, 1);
+
+ /*
+ * Pressing B will toggle the rumble
+ *
+ * if B is pressed but is not held, toggle the rumble
+ */
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_B))
+ wiiuse_toggle_rumble(wm);
+
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_UP))
+ wiiuse_set_ir(wm, 1);
+ if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_DOWN))
+ wiiuse_set_ir(wm, 0);
+
+ /* if the accelerometer is turned on then print angles */
+ if (WIIUSE_USING_ACC(wm)) {
+ printf("wiimote roll = %f [%f]\n", wm->orient.roll, wm->orient.a_roll);
+ printf("wiimote pitch = %f [%f]\n", wm->orient.pitch, wm->orient.a_pitch);
+ printf("wiimote yaw = %f\n", wm->orient.yaw);
+ }
+
+ /*
+ * If IR tracking is enabled then print the coordinates
+ * on the virtual screen that the wiimote is pointing to.
+ *
+ * Also make sure that we see at least 1 dot.
+ */
+ if (WIIUSE_USING_IR(wm)) {
+ int i = 0;
+
+ /* go through each of the 4 possible IR sources */
+ for (; i < 4; ++i) {
+ /* check if the source is visible */
+ if (wm->ir.dot[i].visible)
+ printf("IR source %i: (%u, %u)\n", i, wm->ir.dot[i].x, wm->ir.dot[i].y);
+ }
+
+ printf("IR cursor: (%u, %u)\n", wm->ir.x, wm->ir.y);
+ printf("IR z distance: %f\n", wm->ir.z);
+ }
+
+ /* show events specific to supported expansions */
+ if (wm->exp.type == EXP_NUNCHUK) {
+ /* nunchuk */
+ struct nunchuk_t* nc = (nunchuk_t*)&wm->exp.nunchuk;
+
+ if (IS_PRESSED(nc, NUNCHUK_BUTTON_C)) printf("Nunchuk: C pressed\n");
+ if (IS_PRESSED(nc, NUNCHUK_BUTTON_Z)) printf("Nunchuk: Z pressed\n");
+
+ printf("nunchuk roll = %f\n", nc->orient.roll);
+ printf("nunchuk pitch = %f\n", nc->orient.pitch);
+ printf("nunchuk yaw = %f\n", nc->orient.yaw);
+
+ printf("nunchuk joystick angle: %f\n", nc->js.ang);
+ printf("nunchuk joystick magnitude: %f\n", nc->js.mag);
+ } else if (wm->exp.type == EXP_CLASSIC) {
+ /* classic controller */
+ struct classic_ctrl_t* cc = (classic_ctrl_t*)&wm->exp.classic;
+
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_ZL)) printf("Classic: ZL pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_B)) printf("Classic: B pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_Y)) printf("Classic: Y pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_A)) printf("Classic: A pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_X)) printf("Classic: X pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_ZR)) printf("Classic: ZR pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_LEFT)) printf("Classic: LEFT pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_UP)) printf("Classic: UP pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_RIGHT)) printf("Classic: RIGHT pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_DOWN)) printf("Classic: DOWN pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_FULL_L)) printf("Classic: FULL L pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_MINUS)) printf("Classic: MINUS pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_HOME)) printf("Classic: HOME pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_PLUS)) printf("Classic: PLUS pressed\n");
+ if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_FULL_R)) printf("Classic: FULL R pressed\n");
+
+ printf("classic L button pressed: %f\n", cc->l_shoulder);
+ printf("classic R button pressed: %f\n", cc->r_shoulder);
+ printf("classic left joystick angle: %f\n", cc->ljs.ang);
+ printf("classic left joystick magnitude: %f\n", cc->ljs.mag);
+ printf("classic right joystick angle: %f\n", cc->rjs.ang);
+ printf("classic right joystick magnitude: %f\n", cc->rjs.mag);
+ } else if (wm->exp.type == EXP_GUITAR_HERO_3) {
+ /* guitar hero 3 guitar */
+ struct guitar_hero_3_t* gh3 = (guitar_hero_3_t*)&wm->exp.gh3;
+
+ if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_STRUM_UP)) printf("Guitar: Strum Up pressed\n");
+ if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_STRUM_DOWN)) printf("Guitar: Strum Down pressed\n");
+ if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_YELLOW)) printf("Guitar: Yellow pressed\n");
+ if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_GREEN)) printf("Guitar: Green pressed\n");
+ if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_BLUE)) printf("Guitar: Blue pressed\n");
+ if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_RED)) printf("Guitar: Red pressed\n");
+ if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_ORANGE)) printf("Guitar: Orange pressed\n");
+ if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_PLUS)) printf("Guitar: Plus pressed\n");
+ if (IS_PRESSED(gh3, GUITAR_HERO_3_BUTTON_MINUS)) printf("Guitar: Minus pressed\n");
+
+ printf("Guitar whammy bar: %f\n", gh3->whammy_bar);
+ printf("Guitar joystick angle: %f\n", gh3->js.ang);
+ printf("Guitar joystick magnitude: %f\n", gh3->js.mag);
+ }
+}
+
+
+/**
+ * @brief Callback that handles a read event.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param data Pointer to the filled data block.
+ * @param len Length in bytes of the data block.
+ *
+ * This function is called automatically by the wiiuse library when
+ * the wiimote has returned the full data requested by a previous
+ * call to wiiuse_read_data().
+ *
+ * You can read data on the wiimote, such as Mii data, if
+ * you know the offset address and the length.
+ *
+ * The \a data pointer was specified on the call to wiiuse_read_data().
+ * At the time of this function being called, it is not safe to deallocate
+ * this buffer.
+ */
+void handle_read(struct wiimote_t* wm, byte* data, unsigned short len) {
+ int i = 0;
+
+ printf("\n\n--- DATA READ [wiimote id %i] ---\n", wm->unid);
+ printf("finished read of size %i\n", len);
+ for (; i < len; ++i) {
+ if (!(i%16))
+ printf("\n");
+ printf("%x ", data[i]);
+ }
+ printf("\n\n");
+}
+
+
+/**
+ * @brief Callback that handles a controller status event.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param attachment Is there an attachment? (1 for yes, 0 for no)
+ * @param speaker Is the speaker enabled? (1 for yes, 0 for no)
+ * @param ir Is the IR support enabled? (1 for yes, 0 for no)
+ * @param led What LEDs are lit.
+ * @param battery_level Battery level, between 0.0 (0%) and 1.0 (100%).
+ *
+ * This occurs when either the controller status changed
+ * or the controller status was requested explicitly by
+ * wiiuse_status().
+ *
+ * One reason the status can change is if the nunchuk was
+ * inserted or removed from the expansion port.
+ */
+void handle_ctrl_status(struct wiimote_t* wm) {
+ printf("\n\n--- CONTROLLER STATUS [wiimote id %i] ---\n", wm->unid);
+
+ printf("attachment: %i\n", wm->exp.type);
+ printf("speaker: %i\n", WIIUSE_USING_SPEAKER(wm));
+ printf("ir: %i\n", WIIUSE_USING_IR(wm));
+ printf("leds: %i %i %i %i\n", WIIUSE_IS_LED_SET(wm, 1), WIIUSE_IS_LED_SET(wm, 2), WIIUSE_IS_LED_SET(wm, 3), WIIUSE_IS_LED_SET(wm, 4));
+ printf("battery: %f %%\n", wm->battery_level);
+}
+
+
+/**
+ * @brief Callback that handles a disconnection event.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ *
+ * This can happen if the POWER button is pressed, or
+ * if the connection is interrupted.
+ */
+void handle_disconnect(wiimote* wm) {
+ printf("\n\n--- DISCONNECTED [wiimote id %i] ---\n", wm->unid);
+}
+
+
+void test(struct wiimote_t* wm, byte* data, unsigned short len) {
+ printf("test: %i [%x %x %x %x]\n", len, data[0], data[1], data[2], data[3]);
+}
+
+
+
+/**
+ * @brief main()
+ *
+ * Connect to up to two wiimotes and print any events
+ * that occur on either device.
+ */
+int main(int argc, char** argv) {
+ wiimote** wiimotes;
+ int found, connected;
+
+ /*
+ * Initialize an array of wiimote objects.
+ *
+ * The parameter is the number of wiimotes I want to create.
+ */
+ wiimotes = wiiuse_init(MAX_WIIMOTES);
+
+ /*
+ * Find wiimote devices
+ *
+ * Now we need to find some wiimotes.
+ * Give the function the wiimote array we created, and tell it there
+ * are MAX_WIIMOTES wiimotes we are interested in.
+ *
+ * Set the timeout to be 5 seconds.
+ *
+ * This will return the number of actual wiimotes that are in discovery mode.
+ */
+ found = wiiuse_find(wiimotes, MAX_WIIMOTES, 5);
+ if (!found) {
+ printf ("No wiimotes found.");
+ return 0;
+ }
+
+ /*
+ * Connect to the wiimotes
+ *
+ * Now that we found some wiimotes, connect to them.
+ * Give the function the wiimote array and the number
+ * of wiimote devices we found.
+ *
+ * This will return the number of established connections to the found wiimotes.
+ */
+ connected = wiiuse_connect(wiimotes, MAX_WIIMOTES);
+ if (connected)
+ printf("Connected to %i wiimotes (of %i found).\n", connected, found);
+ else {
+ printf("Failed to connect to any wiimote.\n");
+ return 0;
+ }
+
+ /*
+ * Now set the LEDs and rumble for a second so it's easy
+ * to tell which wiimotes are connected (just like the wii does).
+ */
+ wiiuse_set_leds(wiimotes[0], WIIMOTE_LED_1);
+ wiiuse_set_leds(wiimotes[1], WIIMOTE_LED_2);
+ wiiuse_set_leds(wiimotes[2], WIIMOTE_LED_3);
+ wiiuse_set_leds(wiimotes[3], WIIMOTE_LED_4);
+ wiiuse_rumble(wiimotes[0], 1);
+ wiiuse_rumble(wiimotes[1], 1);
+
+ #ifndef WIN32
+ usleep(200000);
+ #else
+ Sleep(200);
+ #endif
+
+ wiiuse_rumble(wiimotes[0], 0);
+ wiiuse_rumble(wiimotes[1], 0);
+
+ /*
+ * Maybe I'm interested in the battery power of the 0th
+ * wiimote. This should be WIIMOTE_ID_1 but to be sure
+ * you can get the wiimote assoicated with WIIMOTE_ID_1
+ * using the wiiuse_get_by_id() function.
+ *
+ * A status request will return other things too, like
+ * if any expansions are plugged into the wiimote or
+ * what LEDs are lit.
+ */
+ //wiiuse_status(wiimotes[0]);
+
+ /*
+ * This is the main loop
+ *
+ * wiiuse_poll() needs to be called with the wiimote array
+ * and the number of wiimote structures in that array
+ * (it doesn't matter if some of those wiimotes are not used
+ * or are not connected).
+ *
+ * This function will set the event flag for each wiimote
+ * when the wiimote has things to report.
+ */
+ while (1) {
+ if (wiiuse_poll(wiimotes, MAX_WIIMOTES)) {
+ /*
+ * This happens if something happened on any wiimote.
+ * So go through each one and check if anything happened.
+ */
+ int i = 0;
+ for (; i < MAX_WIIMOTES; ++i) {
+ switch (wiimotes[i]->event) {
+ case WIIUSE_EVENT:
+ /* a generic event occured */
+ handle_event(wiimotes[i]);
+ break;
+
+ case WIIUSE_STATUS:
+ /* a status event occured */
+ handle_ctrl_status(wiimotes[i]);
+ break;
+
+ case WIIUSE_DISCONNECT:
+ case WIIUSE_UNEXPECTED_DISCONNECT:
+ /* the wiimote disconnected */
+ handle_disconnect(wiimotes[i]);
+ break;
+
+ case WIIUSE_READ_DATA:
+ /*
+ * Data we requested to read was returned.
+ * Take a look at wiimotes[i]->read_req
+ * for the data.
+ */
+ break;
+
+ case WIIUSE_NUNCHUK_INSERTED:
+ /*
+ * a nunchuk was inserted
+ * This is a good place to set any nunchuk specific
+ * threshold values. By default they are the same
+ * as the wiimote.
+ */
+ //wiiuse_set_nunchuk_orient_threshold((struct nunchuk_t*)&wiimotes[i]->exp.nunchuk, 90.0f);
+ //wiiuse_set_nunchuk_accel_threshold((struct nunchuk_t*)&wiimotes[i]->exp.nunchuk, 100);
+ printf("Nunchuk inserted.\n");
+ break;
+
+ case WIIUSE_CLASSIC_CTRL_INSERTED:
+ printf("Classic controller inserted.\n");
+ break;
+
+ case WIIUSE_GUITAR_HERO_3_CTRL_INSERTED:
+ /* some expansion was inserted */
+ handle_ctrl_status(wiimotes[i]);
+ printf("Guitar Hero 3 controller inserted.\n");
+ break;
+
+ case WIIUSE_NUNCHUK_REMOVED:
+ case WIIUSE_CLASSIC_CTRL_REMOVED:
+ case WIIUSE_GUITAR_HERO_3_CTRL_REMOVED:
+ /* some expansion was removed */
+ handle_ctrl_status(wiimotes[i]);
+ printf("An expansion was removed.\n");
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Disconnect the wiimotes
+ */
+ wiiuse_cleanup(wiimotes, MAX_WIIMOTES);
+
+ return 0;
+}
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.dsp b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.dsp
new file mode 100644
index 0000000000..9e9b34faa8
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.dsp
@@ -0,0 +1,106 @@
+# Microsoft Developer Studio Project File - Name="wiiuseexample" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=wiiuseexample - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "wiiuseexample.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "wiiuseexample.mak" CFG="wiiuseexample - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "wiiuseexample - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "wiiuseexample - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "wiiuseexample - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /W3 /GX /O2 /I "..\..\src" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib wiiuse.lib /nologo /subsystem:console /machine:I386
+
+!ELSEIF "$(CFG)" == "wiiuseexample - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\src" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "wiiuseexample - Win32 Release"
+# Name "wiiuseexample - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\example.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\..\src\wiiuse.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.dsw b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.dsw
new file mode 100644
index 0000000000..e31f8f5615
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "wiiuseexample"=".\wiiuseexample.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.opt b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.opt
new file mode 100644
index 0000000000..fe837f89e2
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/example/msvc/wiiuseexample.opt
Binary files differ
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/Makefile b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/Makefile
new file mode 100644
index 0000000000..bab5b140f4
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/Makefile
@@ -0,0 +1,91 @@
+#
+# wiiuse Makefile
+#
+
+#
+# Change this to your GCC version.
+#
+CC = gcc
+
+####################################################
+#
+# You should not need to edit below this line.
+#
+####################################################
+
+#
+# Universal cflags
+#
+CFLAGS = -Wall -pipe -fPIC -funroll-loops
+
+ifeq ($(debug),1)
+ OBJ_PREFIX = debug
+ CFLAGS += -g -pg -DWITH_WIIUSE_DEBUG
+
+else
+ OBJ_PREFIX = release
+ CFLAGS += -O2
+endif
+
+OBJ_DIR = $(OBJ_PREFIX)-$(shell $(CC) -v 2>&1|grep ^Target:|cut -d' ' -f2)
+
+#
+# Linking flags
+#
+LDFLAGS = -shared -lm -lbluetooth
+
+#
+# Target binaries (always created as BIN)
+#
+BIN = ./$(OBJ_DIR)/libwiiuse.so
+
+#
+# Inclusion paths.
+#
+INCLUDES = -I.
+
+#
+# Generate a list of object files
+#
+OBJS = \
+ $(OBJ_DIR)/classic.o \
+ $(OBJ_DIR)/dynamics.o \
+ $(OBJ_DIR)/events.o \
+ $(OBJ_DIR)/io.o \
+ $(OBJ_DIR)/io_nix.o \
+ $(OBJ_DIR)/ir.o \
+ $(OBJ_DIR)/nunchuk.o \
+ $(OBJ_DIR)/guitar_hero_3.o \
+ $(OBJ_DIR)/wiiuse.o
+
+###############################
+#
+# Build targets.
+#
+###############################
+
+all: $(BIN)
+
+clean:
+ @-rm $(OBJS) 2> /dev/null
+
+distclean:
+ @-rm -r debug-* release-* 2> /dev/null
+
+install:
+ @if [ -e $(BIN) ]; then \
+ cp -v $(BIN) /usr/lib ; \
+ fi
+ @cp -v wiiuse.h /usr/include
+
+$(BIN): mkdir $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) -o $(BIN)
+
+$(OBJ_DIR)/%.o: %.c
+ $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
+
+mkdir:
+ @if [ ! -d $(OBJ_DIR) ]; then \
+ mkdir $(OBJ_DIR); \
+ fi
+
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/classic.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/classic.c
new file mode 100644
index 0000000000..1d2c3ab275
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/classic.c
@@ -0,0 +1,190 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Classic controller expansion device.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#ifdef WIN32
+ #include <Winsock2.h>
+#endif
+
+#include "definitions.h"
+#include "wiiuse_internal.h"
+#include "dynamics.h"
+#include "events.h"
+#include "classic.h"
+
+static void classic_ctrl_pressed_buttons(struct classic_ctrl_t* cc, short now);
+
+/**
+ * @brief Handle the handshake data from the classic controller.
+ *
+ * @param cc A pointer to a classic_ctrl_t structure.
+ * @param data The data read in from the device.
+ * @param len The length of the data block, in bytes.
+ *
+ * @return Returns 1 if handshake was successful, 0 if not.
+ */
+int classic_ctrl_handshake(struct wiimote_t* wm, struct classic_ctrl_t* cc, byte* data, unsigned short len) {
+ int i;
+ int offset = 0;
+
+ cc->btns = 0;
+ cc->btns_held = 0;
+ cc->btns_released = 0;
+ cc->r_shoulder = 0;
+ cc->l_shoulder = 0;
+
+ /* decrypt data */
+ for (i = 0; i < len; ++i)
+ data[i] = (data[i] ^ 0x17) + 0x17;
+
+ if (data[offset] == 0xFF) {
+ /*
+ * Sometimes the data returned here is not correct.
+ * This might happen because the wiimote is lagging
+ * behind our initialization sequence.
+ * To fix this just request the handshake again.
+ *
+ * Other times it's just the first 16 bytes are 0xFF,
+ * but since the next 16 bytes are the same, just use
+ * those.
+ */
+ if (data[offset + 16] == 0xFF) {
+ /* get the calibration data */
+ byte* handshake_buf = malloc(EXP_HANDSHAKE_LEN * sizeof(byte));
+
+ WIIUSE_DEBUG("Classic controller handshake appears invalid, trying again.");
+ wiiuse_read_data_cb(wm, handshake_expansion, handshake_buf, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN);
+
+ return 0;
+ } else
+ offset += 16;
+ }
+
+
+ /* joystick stuff */
+ cc->ljs.max.x = data[0 + offset] / 4;
+ cc->ljs.min.x = data[1 + offset] / 4;
+ cc->ljs.center.x = data[2 + offset] / 4;
+ cc->ljs.max.y = data[3 + offset] / 4;
+ cc->ljs.min.y = data[4 + offset] / 4;
+ cc->ljs.center.y = data[5 + offset] / 4;
+
+ cc->rjs.max.x = data[6 + offset] / 8;
+ cc->rjs.min.x = data[7 + offset] / 8;
+ cc->rjs.center.x = data[8 + offset] / 8;
+ cc->rjs.max.y = data[9 + offset] / 8;
+ cc->rjs.min.y = data[10 + offset] / 8;
+ cc->rjs.center.y = data[11 + offset] / 8;
+
+ /* handshake done */
+ wm->exp.type = EXP_CLASSIC;
+
+ #ifdef WIN32
+ wm->timeout = WIIMOTE_DEFAULT_TIMEOUT;
+ #endif
+
+ return 1;
+}
+
+
+/**
+ * @brief The classic controller disconnected.
+ *
+ * @param cc A pointer to a classic_ctrl_t structure.
+ */
+void classic_ctrl_disconnected(struct classic_ctrl_t* cc) {
+ memset(cc, 0, sizeof(struct classic_ctrl_t));
+}
+
+
+
+/**
+ * @brief Handle classic controller event.
+ *
+ * @param cc A pointer to a classic_ctrl_t structure.
+ * @param msg The message specified in the event packet.
+ */
+void classic_ctrl_event(struct classic_ctrl_t* cc, byte* msg) {
+ int i, lx, ly, rx, ry;
+ byte l, r;
+
+ /* decrypt data */
+ for (i = 0; i < 6; ++i)
+ msg[i] = (msg[i] ^ 0x17) + 0x17;
+
+ classic_ctrl_pressed_buttons(cc, BIG_ENDIAN_SHORT(*(short*)(msg + 4)));
+
+ /* left/right buttons */
+ l = (((msg[2] & 0x60) >> 2) | ((msg[3] & 0xE0) >> 5));
+ r = (msg[3] & 0x1F);
+
+ /*
+ * TODO - LR range hardcoded from 0x00 to 0x1F.
+ * This is probably in the calibration somewhere.
+ */
+ cc->r_shoulder = ((float)r / 0x1F);
+ cc->l_shoulder = ((float)l / 0x1F);
+
+ /* calculate joystick orientation */
+ lx = (msg[0] & 0x3F);
+ ly = (msg[1] & 0x3F);
+ rx = ((msg[0] & 0xC0) >> 3) | ((msg[1] & 0xC0) >> 5) | ((msg[2] & 0x80) >> 7);
+ ry = (msg[2] & 0x1F);
+
+ calc_joystick_state(&cc->ljs, lx, ly);
+ calc_joystick_state(&cc->rjs, rx, ry);
+}
+
+
+/**
+ * @brief Find what buttons are pressed.
+ *
+ * @param cc A pointer to a classic_ctrl_t structure.
+ * @param msg The message byte specified in the event packet.
+ */
+static void classic_ctrl_pressed_buttons(struct classic_ctrl_t* cc, short now) {
+ /* message is inverted (0 is active, 1 is inactive) */
+ now = ~now & CLASSIC_CTRL_BUTTON_ALL;
+
+ /* pressed now & were pressed, then held */
+ cc->btns_held = (now & cc->btns);
+
+ /* were pressed or were held & not pressed now, then released */
+ cc->btns_released = ((cc->btns | cc->btns_held) & ~now);
+
+ /* buttons pressed now */
+ cc->btns = now;
+}
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/classic.h b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/classic.h
new file mode 100644
index 0000000000..356f6a458b
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/classic.h
@@ -0,0 +1,53 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Classic controller expansion device.
+ */
+
+#ifndef CLASSIC_H_INCLUDED
+#define CLASSIC_H_INCLUDED
+
+#include "wiiuse_internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int classic_ctrl_handshake(struct wiimote_t* wm, struct classic_ctrl_t* cc, byte* data, unsigned short len);
+
+void classic_ctrl_disconnected(struct classic_ctrl_t* cc);
+
+void classic_ctrl_event(struct classic_ctrl_t* cc, byte* msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // CLASSIC_H_INCLUDED
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/definitions.h b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/definitions.h
new file mode 100644
index 0000000000..5a8da85def
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/definitions.h
@@ -0,0 +1,79 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief General definitions.
+ */
+
+#ifndef DEFINITIONS_H_INCLUDED
+#define DEFINITIONS_H_INCLUDED
+
+/* this is wiiuse - used to distinguish from third party programs using wiiuse.h */
+#include "os.h"
+
+#define WIIMOTE_PI 3.14159265f
+
+//#define WITH_WIIUSE_DEBUG
+
+/* Error output macros */
+#define WIIUSE_ERROR(fmt, ...) fprintf(stderr, "[ERROR] " fmt "\n", ##__VA_ARGS__)
+
+/* Warning output macros */
+#define WIIUSE_WARNING(fmt, ...) fprintf(stderr, "[WARNING] " fmt "\n", ##__VA_ARGS__)
+
+/* Information output macros */
+#define WIIUSE_INFO(fmt, ...) fprintf(stderr, "[INFO] " fmt "\n", ##__VA_ARGS__)
+
+#ifdef WITH_WIIUSE_DEBUG
+ #ifdef WIN32
+ #define WIIUSE_DEBUG(fmt, ...) do { \
+ char* file = __FILE__; \
+ int i = strlen(file) - 1; \
+ for (; i && (file[i] != '\\'); --i); \
+ fprintf(stderr, "[DEBUG] %s:%i: " fmt "\n", file+i+1, __LINE__, ##__VA_ARGS__); \
+ } while (0)
+ #else
+ #define WIIUSE_DEBUG(fmt, ...) fprintf(stderr, "[DEBUG] " __FILE__ ":%i: " fmt "\n", __LINE__, ##__VA_ARGS__)
+ #endif
+#else
+ #define WIIUSE_DEBUG(fmt, ...)
+#endif
+
+/* Convert between radians and degrees */
+#define RAD_TO_DEGREE(r) ((r * 180.0f) / WIIMOTE_PI)
+#define DEGREE_TO_RAD(d) (d * (WIIMOTE_PI / 180.0f))
+
+/* Convert to big endian */
+#define BIG_ENDIAN_LONG(i) (htonl(i))
+#define BIG_ENDIAN_SHORT(i) (htons(i))
+
+#define absf(x) ((x >= 0) ? (x) : (x * -1.0f))
+#define diff_f(x, y) ((x >= y) ? (absf(x - y)) : (absf(y - x)))
+
+#endif // DEFINITIONS_H_INCLUDED
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/dynamics.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/dynamics.c
new file mode 100644
index 0000000000..53612a6b90
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/dynamics.c
@@ -0,0 +1,228 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Handles the dynamics of the wiimote.
+ *
+ * The file includes functions that handle the dynamics
+ * of the wiimote. Such dynamics include orientation and
+ * motion sensing.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#ifdef WIN32
+ #include <float.h>
+#endif
+
+#include "definitions.h"
+#include "wiiuse_internal.h"
+#include "ir.h"
+#include "dynamics.h"
+
+/**
+ * @brief Calculate the roll, pitch, yaw.
+ *
+ * @param ac An accelerometer (accel_t) structure.
+ * @param accel [in] Pointer to a vec3b_t structure that holds the raw acceleration data.
+ * @param orient [out] Pointer to a orient_t structure that will hold the orientation data.
+ * @param rorient [out] Pointer to a orient_t structure that will hold the non-smoothed orientation data.
+ * @param smooth If smoothing should be performed on the angles calculated. 1 to enable, 0 to disable.
+ *
+ * Given the raw acceleration data from the accelerometer struct, calculate
+ * the orientation of the device and set it in the \a orient parameter.
+ */
+void calculate_orientation(struct accel_t* ac, struct vec3b_t* accel, struct orient_t* orient, int smooth) {
+ float xg, yg, zg;
+ float x, y, z;
+
+ /*
+ * roll - use atan(z / x) [ ranges from -180 to 180 ]
+ * pitch - use atan(z / y) [ ranges from -180 to 180 ]
+ * yaw - impossible to tell without IR
+ */
+
+ /* yaw - set to 0, IR will take care of it if it's enabled */
+ orient->yaw = 0.0f;
+
+ /* find out how much it has to move to be 1g */
+ xg = (float)ac->cal_g.x;
+ yg = (float)ac->cal_g.y;
+ zg = (float)ac->cal_g.z;
+
+ /* find out how much it actually moved and normalize to +/- 1g */
+ x = ((float)accel->x - (float)ac->cal_zero.x) / xg;
+ y = ((float)accel->y - (float)ac->cal_zero.y) / yg;
+ z = ((float)accel->z - (float)ac->cal_zero.z) / zg;
+
+ /* make sure x,y,z are between -1 and 1 for the tan functions */
+ if (x < -1.0f) x = -1.0f;
+ else if (x > 1.0f) x = 1.0f;
+ if (y < -1.0f) y = -1.0f;
+ else if (y > 1.0f) y = 1.0f;
+ if (z < -1.0f) z = -1.0f;
+ else if (z > 1.0f) z = 1.0f;
+
+ /* if it is over 1g then it is probably accelerating and not reliable */
+ if (abs(accel->x - ac->cal_zero.x) <= ac->cal_g.x) {
+ /* roll */
+ x = RAD_TO_DEGREE(atan2f(x, z));
+
+ orient->roll = x;
+ orient->a_roll = x;
+ }
+
+ if (abs(accel->y - ac->cal_zero.y) <= ac->cal_g.y) {
+ /* pitch */
+ y = RAD_TO_DEGREE(atan2f(y, z));
+
+ orient->pitch = y;
+ orient->a_pitch = y;
+ }
+
+ /* smooth the angles if enabled */
+ if (smooth) {
+ apply_smoothing(ac, orient, SMOOTH_ROLL);
+ apply_smoothing(ac, orient, SMOOTH_PITCH);
+ }
+}
+
+
+/**
+ * @brief Calculate the gravity forces on each axis.
+ *
+ * @param ac An accelerometer (accel_t) structure.
+ * @param accel [in] Pointer to a vec3b_t structure that holds the raw acceleration data.
+ * @param gforce [out] Pointer to a gforce_t structure that will hold the gravity force data.
+ */
+void calculate_gforce(struct accel_t* ac, struct vec3b_t* accel, struct gforce_t* gforce) {
+ float xg, yg, zg;
+
+ /* find out how much it has to move to be 1g */
+ xg = (float)ac->cal_g.x;
+ yg = (float)ac->cal_g.y;
+ zg = (float)ac->cal_g.z;
+
+ /* find out how much it actually moved and normalize to +/- 1g */
+ gforce->x = ((float)accel->x - (float)ac->cal_zero.x) / xg;
+ gforce->y = ((float)accel->y - (float)ac->cal_zero.y) / yg;
+ gforce->z = ((float)accel->z - (float)ac->cal_zero.z) / zg;
+}
+
+
+/**
+ * @brief Calculate the angle and magnitude of a joystick.
+ *
+ * @param js [out] Pointer to a joystick_t structure.
+ * @param x The raw x-axis value.
+ * @param y The raw y-axis value.
+ */
+void calc_joystick_state(struct joystick_t* js, float x, float y) {
+ float rx, ry, ang;
+
+ /*
+ * Since the joystick center may not be exactly:
+ * (min + max) / 2
+ * Then the range from the min to the center and the center to the max
+ * may be different.
+ * Because of this, depending on if the current x or y value is greater
+ * or less than the assoicated axis center value, it needs to be interpolated
+ * between the center and the minimum or maxmimum rather than between
+ * the minimum and maximum.
+ *
+ * So we have something like this:
+ * (x min) [-1] ---------*------ [0] (x center) [0] -------- [1] (x max)
+ * Where the * is the current x value.
+ * The range is therefore -1 to 1, 0 being the exact center rather than
+ * the middle of min and max.
+ */
+ if (x == js->center.x)
+ rx = 0;
+ else if (x >= js->center.x)
+ rx = ((float)(x - js->center.x) / (float)(js->max.x - js->center.x));
+ else
+ rx = ((float)(x - js->min.x) / (float)(js->center.x - js->min.x)) - 1.0f;
+
+ if (y == js->center.y)
+ ry = 0;
+ else if (y >= js->center.y)
+ ry = ((float)(y - js->center.y) / (float)(js->max.y - js->center.y));
+ else
+ ry = ((float)(y - js->min.y) / (float)(js->center.y - js->min.y)) - 1.0f;
+
+ /* calculate the joystick angle and magnitude */
+ ang = RAD_TO_DEGREE(atanf(ry / rx));
+ ang -= 90.0f;
+ if (rx < 0.0f)
+ ang -= 180.0f;
+ js->ang = absf(ang);
+ js->mag = (float) sqrt((rx * rx) + (ry * ry));
+}
+
+
+void apply_smoothing(struct accel_t* ac, struct orient_t* orient, int type) {
+ switch (type) {
+ case SMOOTH_ROLL:
+ {
+ /* it's possible last iteration was nan or inf, so set it to 0 if that happened */
+ if (isnan(ac->st_roll) || isinf(ac->st_roll))
+ ac->st_roll = 0.0f;
+
+ /*
+ * If the sign changes (which will happen if going from -180 to 180)
+ * or from (-1 to 1) then don't smooth, just use the new angle.
+ */
+ if (((ac->st_roll < 0) && (orient->roll > 0)) || ((ac->st_roll > 0) && (orient->roll < 0))) {
+ ac->st_roll = orient->roll;
+ } else {
+ orient->roll = ac->st_roll + (ac->st_alpha * (orient->a_roll - ac->st_roll));
+ ac->st_roll = orient->roll;
+ }
+
+ return;
+ }
+
+ case SMOOTH_PITCH:
+ {
+ if (isnan(ac->st_pitch) || isinf(ac->st_pitch))
+ ac->st_pitch = 0.0f;
+
+ if (((ac->st_pitch < 0) && (orient->pitch > 0)) || ((ac->st_pitch > 0) && (orient->pitch < 0))) {
+ ac->st_pitch = orient->pitch;
+ } else {
+ orient->pitch = ac->st_pitch + (ac->st_alpha * (orient->a_pitch - ac->st_pitch));
+ ac->st_pitch = orient->pitch;
+ }
+
+ return;
+ }
+ }
+}
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/dynamics.h b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/dynamics.h
new file mode 100644
index 0000000000..2a8f96500b
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/dynamics.h
@@ -0,0 +1,56 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Handles the dynamics of the wiimote.
+ *
+ * The file includes functions that handle the dynamics
+ * of the wiimote. Such dynamics include orientation and
+ * motion sensing.
+ */
+
+#ifndef DYNAMICS_H_INCLUDED
+#define DYNAMICS_H_INCLUDED
+
+#include "wiiuse_internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void calculate_orientation(struct accel_t* ac, struct vec3b_t* accel, struct orient_t* orient, int smooth);
+void calculate_gforce(struct accel_t* ac, struct vec3b_t* accel, struct gforce_t* gforce);
+void calc_joystick_state(struct joystick_t* js, float x, float y);
+void apply_smoothing(struct accel_t* ac, struct orient_t* orient, int type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // DYNAMICS_H_INCLUDED
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/events.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/events.c
new file mode 100644
index 0000000000..6668eda5c8
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/events.c
@@ -0,0 +1,878 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Handles wiimote events.
+ *
+ * The file includes functions that handle the events
+ * that are sent from the wiimote to us.
+ */
+
+#include <stdio.h>
+
+#ifndef WIN32
+ #include <sys/time.h>
+ #include <unistd.h>
+ #include <errno.h>
+#else
+ #include <winsock2.h>
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "definitions.h"
+#include "io.h"
+#include "wiiuse_internal.h"
+#include "dynamics.h"
+#include "ir.h"
+#include "nunchuk.h"
+#include "classic.h"
+#include "guitar_hero_3.h"
+#include "events.h"
+
+static void idle_cycle(struct wiimote_t* wm);
+static void clear_dirty_reads(struct wiimote_t* wm);
+static void propagate_event(struct wiimote_t* wm, byte event, byte* msg);
+static void event_data_read(struct wiimote_t* wm, byte* msg);
+static void event_status(struct wiimote_t* wm, byte* msg);
+static void handle_expansion(struct wiimote_t* wm, byte* msg);
+
+static void save_state(struct wiimote_t* wm);
+static int state_changed(struct wiimote_t* wm);
+
+/**
+ * @brief Poll the wiimotes for any events.
+ *
+ * @param wm An array of pointers to wiimote_t structures.
+ * @param wiimotes The number of wiimote_t structures in the \a wm array.
+ *
+ * @return Returns number of wiimotes that an event has occured on.
+ *
+ * It is necessary to poll the wiimote devices for events
+ * that occur. If an event occurs on a particular wiimote,
+ * the event variable will be set.
+ */
+int wiiuse_poll(struct wiimote_t** wm, int wiimotes) {
+ int evnt = 0;
+
+ #ifndef WIN32
+ /*
+ * *nix
+ */
+ struct timeval tv;
+ fd_set fds;
+ int r;
+ int i;
+ int highest_fd = -1;
+
+ if (!wm) return 0;
+
+ /* block select() for 1/2000th of a second */
+ tv.tv_sec = 0;
+ tv.tv_usec = 500;
+
+ FD_ZERO(&fds);
+
+ for (i = 0; i < wiimotes; ++i) {
+ /* only poll it if it is connected */
+ if (WIIMOTE_IS_SET(wm[i], WIIMOTE_STATE_CONNECTED)) {
+ FD_SET(wm[i]->in_sock, &fds);
+
+ /* find the highest fd of the connected wiimotes */
+ if (wm[i]->in_sock > highest_fd)
+ highest_fd = wm[i]->in_sock;
+ }
+
+ wm[i]->event = WIIUSE_NONE;
+ }
+
+ if (highest_fd == -1)
+ /* nothing to poll */
+ return 0;
+
+ if (select(highest_fd + 1, &fds, NULL, NULL, &tv) == -1) {
+ WIIUSE_ERROR("Unable to select() the wiimote interrupt socket(s).");
+ perror("Error Details");
+ return 0;
+ }
+
+ /* check each socket for an event */
+ for (i = 0; i < wiimotes; ++i) {
+ /* if this wiimote is not connected, skip it */
+ if (!WIIMOTE_IS_CONNECTED(wm[i]))
+ continue;
+
+ if (FD_ISSET(wm[i]->in_sock, &fds)) {
+ /* clear out the event buffer */
+ memset(wm[i]->event_buf, 0, sizeof(wm[i]->event_buf));
+
+ /* clear out any old read requests */
+ clear_dirty_reads(wm[i]);
+
+ /* read the pending message into the buffer */
+ r = read(wm[i]->in_sock, wm[i]->event_buf, sizeof(wm[i]->event_buf));
+ if (r == -1) {
+ /* error reading data */
+ WIIUSE_ERROR("Receiving wiimote data (id %i).", wm[i]->unid);
+ perror("Error Details");
+
+ if (errno == ENOTCONN) {
+ /* this can happen if the bluetooth dongle is disconnected */
+ WIIUSE_ERROR("Bluetooth appears to be disconnected. Wiimote unid %i will be disconnected.", wm[i]->unid);
+ wiiuse_disconnect(wm[i]);
+ wm[i]->event = WIIUSE_UNEXPECTED_DISCONNECT;
+ }
+
+ continue;
+ }
+ if (!r) {
+ /* remote disconnect */
+ wiiuse_disconnected(wm[i]);
+ evnt = 1;
+ continue;
+ }
+
+ /* propagate the event */
+ propagate_event(wm[i], wm[i]->event_buf[1], wm[i]->event_buf+2);
+ evnt += (wm[i]->event != WIIUSE_NONE);
+ } else {
+ idle_cycle(wm[i]);
+ }
+ }
+ #else
+ /*
+ * Windows
+ */
+ int i;
+
+ if (!wm) return 0;
+
+ for (i = 0; i < wiimotes; ++i) {
+ wm[i]->event = WIIUSE_NONE;
+
+ if (wiiuse_io_read(wm[i])) {
+ /* propagate the event */
+ propagate_event(wm[i], wm[i]->event_buf[0], wm[i]->event_buf+1);
+ evnt += (wm[i]->event != WIIUSE_NONE);
+
+ /* clear out the event buffer */
+ memset(wm[i]->event_buf, 0, sizeof(wm[i]->event_buf));
+ } else {
+ idle_cycle(wm[i]);
+ }
+ }
+ #endif
+
+ return evnt;
+}
+
+
+/**
+ * @brief Called on a cycle where no significant change occurs.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ */
+static void idle_cycle(struct wiimote_t* wm) {
+ /*
+ * Smooth the angles.
+ *
+ * This is done to make sure that on every cycle the orientation
+ * angles are smoothed. Normally when an event occurs the angles
+ * are updated and smoothed, but if no packet comes in then the
+ * angles remain the same. This means the angle wiiuse reports
+ * is still an old value. Smoothing needs to be applied in this
+ * case in order for the angle it reports to converge to the true
+ * angle of the device.
+ */
+ if (WIIUSE_USING_ACC(wm) && WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING)) {
+ apply_smoothing(&wm->accel_calib, &wm->orient, SMOOTH_ROLL);
+ apply_smoothing(&wm->accel_calib, &wm->orient, SMOOTH_PITCH);
+ }
+
+ /* clear out any old read requests */
+ clear_dirty_reads(wm);
+}
+
+
+/**
+ * @brief Clear out all old 'dirty' read requests.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ */
+static void clear_dirty_reads(struct wiimote_t* wm) {
+ struct read_req_t* req = wm->read_req;
+
+ while (req && req->dirty) {
+ WIIUSE_DEBUG("Cleared old read request for address: %x", req->addr);
+
+ wm->read_req = req->next;
+ free(req);
+ req = wm->read_req;
+ }
+}
+
+
+/**
+ * @brief Analyze the event that occured on a wiimote.
+ *
+ * @param wm An array of pointers to wiimote_t structures.
+ * @param event The event that occured.
+ * @param msg The message specified in the event packet.
+ *
+ * Pass the event to the registered event callback.
+ */
+static void propagate_event(struct wiimote_t* wm, byte event, byte* msg) {
+ save_state(wm);
+
+ switch (event) {
+ case WM_RPT_BTN:
+ {
+ /* button */
+ wiiuse_pressed_buttons(wm, msg);
+ break;
+ }
+ case WM_RPT_BTN_ACC:
+ {
+ /* button - motion */
+ wiiuse_pressed_buttons(wm, msg);
+
+ wm->accel.x = msg[2];
+ wm->accel.y = msg[3];
+ wm->accel.z = msg[4];
+
+ /* calculate the remote orientation */
+ calculate_orientation(&wm->accel_calib, &wm->accel, &wm->orient, WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING));
+
+ /* calculate the gforces on each axis */
+ calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce);
+
+ break;
+ }
+ case WM_RPT_READ:
+ {
+ /* data read */
+ event_data_read(wm, msg);
+
+ /* yeah buttons may be pressed, but this wasn't an "event" */
+ return;
+ }
+ case WM_RPT_CTRL_STATUS:
+ {
+ /* controller status */
+ event_status(wm, msg);
+
+ /* don't execute the event callback */
+ return;
+ }
+ case WM_RPT_BTN_EXP:
+ {
+ /* button - expansion */
+ wiiuse_pressed_buttons(wm, msg);
+ handle_expansion(wm, msg+2);
+
+ break;
+ }
+ case WM_RPT_BTN_ACC_EXP:
+ {
+ /* button - motion - expansion */
+ wiiuse_pressed_buttons(wm, msg);
+
+ wm->accel.x = msg[2];
+ wm->accel.y = msg[3];
+ wm->accel.z = msg[4];
+
+ calculate_orientation(&wm->accel_calib, &wm->accel, &wm->orient, WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING));
+ calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce);
+
+ handle_expansion(wm, msg+5);
+
+ break;
+ }
+ case WM_RPT_BTN_ACC_IR:
+ {
+ /* button - motion - ir */
+ wiiuse_pressed_buttons(wm, msg);
+
+ wm->accel.x = msg[2];
+ wm->accel.y = msg[3];
+ wm->accel.z = msg[4];
+
+ calculate_orientation(&wm->accel_calib, &wm->accel, &wm->orient, WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING));
+ calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce);
+
+ /* ir */
+ calculate_extended_ir(wm, msg+5);
+
+ break;
+ }
+ case WM_RPT_BTN_IR_EXP:
+ {
+ /* button - ir - expansion */
+ wiiuse_pressed_buttons(wm, msg);
+ handle_expansion(wm, msg+12);
+
+ /* ir */
+ calculate_basic_ir(wm, msg+2);
+
+ break;
+ }
+ case WM_RPT_BTN_ACC_IR_EXP:
+ {
+ /* button - motion - ir - expansion */
+ wiiuse_pressed_buttons(wm, msg);
+
+ wm->accel.x = msg[2];
+ wm->accel.y = msg[3];
+ wm->accel.z = msg[4];
+
+ calculate_orientation(&wm->accel_calib, &wm->accel, &wm->orient, WIIMOTE_IS_FLAG_SET(wm, WIIUSE_SMOOTHING));
+ calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce);
+
+ handle_expansion(wm, msg+15);
+
+ /* ir */
+ calculate_basic_ir(wm, msg+5);
+
+ break;
+ }
+ case WM_RPT_WRITE:
+ {
+ /* write feedback - safe to skip */
+ break;
+ }
+ default:
+ {
+ WIIUSE_WARNING("Unknown event, can not handle it [Code 0x%x].", event);
+ return;
+ }
+ }
+
+ /* was there an event? */
+ if (state_changed(wm))
+ wm->event = WIIUSE_EVENT;
+}
+
+
+/**
+ * @brief Find what buttons are pressed.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param msg The message specified in the event packet.
+ */
+void wiiuse_pressed_buttons(struct wiimote_t* wm, byte* msg) {
+ short now;
+
+ /* convert to big endian */
+ now = BIG_ENDIAN_SHORT(*(short*)msg) & WIIMOTE_BUTTON_ALL;
+
+ /* pressed now & were pressed, then held */
+ wm->btns_held = (now & wm->btns);
+
+ /* were pressed or were held & not pressed now, then released */
+ wm->btns_released = ((wm->btns | wm->btns_held) & ~now);
+
+ /* buttons pressed now */
+ wm->btns = now;
+}
+
+
+/**
+ * @brief Received a data packet from a read request.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param msg The message specified in the event packet.
+ *
+ * Data from the wiimote comes in packets. If the requested
+ * data segment size is bigger than one packet can hold then
+ * several packets will be received. These packets are first
+ * reassembled into one, then the registered callback function
+ * that handles data reads is invoked.
+ */
+static void event_data_read(struct wiimote_t* wm, byte* msg) {
+ /* we must always assume the packet received is from the most recent request */
+ byte err;
+ byte len;
+ unsigned short offset;
+ struct read_req_t* req = wm->read_req;
+
+ wiiuse_pressed_buttons(wm, msg);
+
+ /* find the next non-dirty request */
+ while (req && req->dirty)
+ req = req->next;
+
+ /* if we don't have a request out then we didn't ask for this packet */
+ if (!req) {
+ WIIUSE_WARNING("Received data packet when no request was made.");
+ return;
+ }
+
+ err = msg[2] & 0x0F;
+
+ if (err == 0x08)
+ WIIUSE_WARNING("Unable to read data - address does not exist.");
+ else if (err == 0x07)
+ WIIUSE_WARNING("Unable to read data - address is for write-only registers.");
+ else if (err)
+ WIIUSE_WARNING("Unable to read data - unknown error code %x.", err);
+
+ if (err) {
+ /* this request errored out, so skip it and go to the next one */
+
+ /* delete this request */
+ wm->read_req = req->next;
+ free(req);
+
+ /* if another request exists send it to the wiimote */
+ if (wm->read_req)
+ wiiuse_send_next_pending_read_request(wm);
+
+ return;
+ }
+
+ len = ((msg[2] & 0xF0) >> 4) + 1;
+ offset = BIG_ENDIAN_SHORT(*(unsigned short*)(msg + 3));
+ req->addr = (req->addr & 0xFFFF);
+
+ req->wait -= len;
+ if (req->wait >= req->size)
+ /* this should never happen */
+ req->wait = 0;
+
+ WIIUSE_DEBUG("Received read packet:");
+ WIIUSE_DEBUG(" Packet read offset: %i bytes", offset);
+ WIIUSE_DEBUG(" Request read offset: %i bytes", req->addr);
+ WIIUSE_DEBUG(" Read offset into buf: %i bytes", offset - req->addr);
+ WIIUSE_DEBUG(" Read data size: %i bytes", len);
+ WIIUSE_DEBUG(" Still need: %i bytes", req->wait);
+
+ /* reconstruct this part of the data */
+ memcpy((req->buf + offset - req->addr), (msg + 5), len);
+
+ #ifdef WITH_WIIUSE_DEBUG
+ {
+ int i = 0;
+ printf("Read: ");
+ for (; i < req->size - req->wait; ++i)
+ printf("%x ", req->buf[i]);
+ printf("\n");
+ }
+ #endif
+
+ /* if all data has been received, execute the read event callback or generate event */
+ if (!req->wait) {
+ if (req->cb) {
+ /* this was a callback, so invoke it now */
+ req->cb(wm, req->buf, req->size);
+
+ /* delete this request */
+ wm->read_req = req->next;
+ free(req);
+ } else {
+ /*
+ * This should generate an event.
+ * We need to leave the event in the array so the client
+ * can access it still. We'll flag is as being 'dirty'
+ * and give the client one cycle to use it. Next event
+ * we will remove it from the list.
+ */
+ wm->event = WIIUSE_READ_DATA;
+ req->dirty = 1;
+ }
+
+ /* if another request exists send it to the wiimote */
+ if (wm->read_req)
+ wiiuse_send_next_pending_read_request(wm);
+ }
+}
+
+
+/**
+ * @brief Read the controller status.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param msg The message specified in the event packet.
+ *
+ * Read the controller status and execute the registered status callback.
+ */
+static void event_status(struct wiimote_t* wm, byte* msg) {
+ int led[4] = {0};
+ int attachment = 0;
+ int ir = 0;
+ int exp_changed = 0;
+
+ /*
+ * An event occured.
+ * This event can be overwritten by a more specific
+ * event type during a handshake or expansion removal.
+ */
+ wm->event = WIIUSE_STATUS;
+
+ wiiuse_pressed_buttons(wm, msg);
+
+ /* find what LEDs are lit */
+ if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_1) led[0] = 1;
+ if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_2) led[1] = 1;
+ if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_3) led[2] = 1;
+ if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_4) led[3] = 1;
+
+ /* is an attachment connected to the expansion port? */
+ if ((msg[2] & WM_CTRL_STATUS_BYTE1_ATTACHMENT) == WM_CTRL_STATUS_BYTE1_ATTACHMENT)
+ attachment = 1;
+
+ /* is the speaker enabled? */
+ if ((msg[2] & WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED) == WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED)
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_SPEAKER);
+
+ /* is IR sensing enabled? */
+ if ((msg[2] & WM_CTRL_STATUS_BYTE1_IR_ENABLED) == WM_CTRL_STATUS_BYTE1_IR_ENABLED)
+ ir = 1;
+
+ /* find the battery level and normalize between 0 and 1 */
+ wm->battery_level = (msg[5] / (float)WM_MAX_BATTERY_CODE);
+
+ /* expansion port */
+ if (attachment && !WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) {
+ /* send the initialization code for the attachment */
+ handshake_expansion(wm, NULL, 0);
+ exp_changed = 1;
+ } else if (!attachment && WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) {
+ /* attachment removed */
+ disable_expansion(wm);
+ exp_changed = 1;
+ }
+
+ #ifdef WIN32
+ if (!attachment) {
+ WIIUSE_DEBUG("Setting timeout to normal %i ms.", wm->normal_timeout);
+ wm->timeout = wm->normal_timeout;
+ }
+ #endif
+
+ /*
+ * From now on the remote will only send status packets.
+ * We need to send a WIIMOTE_CMD_REPORT_TYPE packet to
+ * reenable other incoming reports.
+ */
+ if (exp_changed && WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) {
+ /*
+ * Since the expansion status changed IR needs to
+ * be reset for the new IR report mode.
+ */
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR);
+ wiiuse_set_ir(wm, 1);
+ } else
+ wiiuse_set_report_type(wm);
+}
+
+
+/**
+ * @brief Handle data from the expansion.
+ *
+ * @param wm A pointer to a wiimote_t structure.
+ * @param msg The message specified in the event packet for the expansion.
+ */
+static void handle_expansion(struct wiimote_t* wm, byte* msg) {
+ switch (wm->exp.type) {
+ case EXP_NUNCHUK:
+ nunchuk_event(&wm->exp.nunchuk, msg);
+ break;
+ case EXP_CLASSIC:
+ classic_ctrl_event(&wm->exp.classic, msg);
+ break;
+ case EXP_GUITAR_HERO_3:
+ guitar_hero_3_event(&wm->exp.gh3, msg);
+ break;
+ default:
+ break;
+ }
+}
+
+
+/**
+ * @brief Handle the handshake data from the expansion device.
+ *
+ * @param wm A pointer to a wiimote_t structure.
+ * @param data The data read in from the device.
+ * @param len The length of the data block, in bytes.
+ *
+ * Tries to determine what kind of expansion was attached
+ * and invoke the correct handshake function.
+ *
+ * If the data is NULL then this function will try to start
+ * a handshake with the expansion.
+ */
+void handshake_expansion(struct wiimote_t* wm, byte* data, unsigned short len) {
+ int id;
+
+ if (!data) {
+ byte* handshake_buf;
+ byte buf = 0x00;
+
+ if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP))
+ disable_expansion(wm);
+
+ /* increase the timeout until the handshake completes */
+ #ifdef WIN32
+ WIIUSE_DEBUG("Setting timeout to expansion %i ms.", wm->exp_timeout);
+ wm->timeout = wm->exp_timeout;
+ #endif
+
+ wiiuse_write_data(wm, WM_EXP_MEM_ENABLE, &buf, 1);
+
+ /* get the calibration data */
+ handshake_buf = malloc(EXP_HANDSHAKE_LEN * sizeof(byte));
+ wiiuse_read_data_cb(wm, handshake_expansion, handshake_buf, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN);
+
+ /* tell the wiimote to send expansion data */
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_EXP);
+
+ return;
+ }
+
+ id = BIG_ENDIAN_LONG(*(int*)(data + 220));
+
+ /* call the corresponding handshake function for this expansion */
+ switch (id) {
+ case EXP_ID_CODE_NUNCHUK:
+ {
+ if (nunchuk_handshake(wm, &wm->exp.nunchuk, data, len))
+ wm->event = WIIUSE_NUNCHUK_INSERTED;
+ break;
+ }
+ case EXP_ID_CODE_CLASSIC_CONTROLLER:
+ {
+ if (classic_ctrl_handshake(wm, &wm->exp.classic, data, len))
+ wm->event = WIIUSE_CLASSIC_CTRL_INSERTED;
+ break;
+ }
+ case EXP_ID_CODE_GUITAR:
+ {
+ if (guitar_hero_3_handshake(wm, &wm->exp.gh3, data, len))
+ wm->event = WIIUSE_GUITAR_HERO_3_CTRL_INSERTED;
+ break;
+ }
+ default:
+ {
+ WIIUSE_WARNING("Unknown expansion type. Code: 0x%x", id);
+ break;
+ }
+ }
+
+ free(data);
+}
+
+
+
+/**
+ * @brief Disable the expansion device if it was enabled.
+ *
+ * @param wm A pointer to a wiimote_t structure.
+ * @param data The data read in from the device.
+ * @param len The length of the data block, in bytes.
+ *
+ * If the data is NULL then this function will try to start
+ * a handshake with the expansion.
+ */
+void disable_expansion(struct wiimote_t* wm) {
+ if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP))
+ return;
+
+ /* tell the assoicated module the expansion was removed */
+ switch (wm->exp.type) {
+ case EXP_NUNCHUK:
+ nunchuk_disconnected(&wm->exp.nunchuk);
+ wm->event = WIIUSE_NUNCHUK_REMOVED;
+ break;
+ case EXP_CLASSIC:
+ classic_ctrl_disconnected(&wm->exp.classic);
+ wm->event = WIIUSE_CLASSIC_CTRL_REMOVED;
+ break;
+ case EXP_GUITAR_HERO_3:
+ guitar_hero_3_disconnected(&wm->exp.gh3);
+ wm->event = WIIUSE_GUITAR_HERO_3_CTRL_REMOVED;
+ break;
+ default:
+ break;
+ }
+
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP);
+ wm->exp.type = EXP_NONE;
+}
+
+
+/**
+ * @brief Save important state data.
+ * @param wm A pointer to a wiimote_t structure.
+ */
+static void save_state(struct wiimote_t* wm) {
+ /* wiimote */
+ wm->lstate.btns = wm->btns;
+ wm->lstate.accel = wm->accel;
+
+ /* ir */
+ if (WIIUSE_USING_IR(wm)) {
+ wm->lstate.ir_ax = wm->ir.ax;
+ wm->lstate.ir_ay = wm->ir.ay;
+ wm->lstate.ir_distance = wm->ir.distance;
+ }
+
+ /* expansion */
+ switch (wm->exp.type) {
+ case EXP_NUNCHUK:
+ wm->lstate.exp_ljs_ang = wm->exp.nunchuk.js.ang;
+ wm->lstate.exp_ljs_mag = wm->exp.nunchuk.js.mag;
+ wm->lstate.exp_btns = wm->exp.nunchuk.btns;
+ wm->lstate.exp_accel = wm->exp.nunchuk.accel;
+ break;
+
+ case EXP_CLASSIC:
+ wm->lstate.exp_ljs_ang = wm->exp.classic.ljs.ang;
+ wm->lstate.exp_ljs_mag = wm->exp.classic.ljs.mag;
+ wm->lstate.exp_rjs_ang = wm->exp.classic.rjs.ang;
+ wm->lstate.exp_rjs_mag = wm->exp.classic.rjs.mag;
+ wm->lstate.exp_r_shoulder = wm->exp.classic.r_shoulder;
+ wm->lstate.exp_l_shoulder = wm->exp.classic.l_shoulder;
+ wm->lstate.exp_btns = wm->exp.classic.btns;
+ break;
+
+ case EXP_GUITAR_HERO_3:
+ wm->lstate.exp_ljs_ang = wm->exp.gh3.js.ang;
+ wm->lstate.exp_ljs_mag = wm->exp.gh3.js.mag;
+ wm->lstate.exp_r_shoulder = wm->exp.gh3.whammy_bar;
+ wm->lstate.exp_btns = wm->exp.gh3.btns;
+ break;
+
+ case EXP_NONE:
+ break;
+ }
+}
+
+
+/**
+ * @brief Determine if the current state differs significantly from the previous.
+ * @param wm A pointer to a wiimote_t structure.
+ * @return 1 if a significant change occured, 0 if not.
+ */
+static int state_changed(struct wiimote_t* wm) {
+ #define STATE_CHANGED(a, b) if (a != b) return 1
+
+ #define CROSS_THRESH(last, now, thresh) \
+ do { \
+ if (WIIMOTE_IS_FLAG_SET(wm, WIIUSE_ORIENT_THRESH)) { \
+ if ((diff_f(last.roll, now.roll) >= thresh) || \
+ (diff_f(last.pitch, now.pitch) >= thresh) || \
+ (diff_f(last.yaw, now.yaw) >= thresh)) \
+ { \
+ last = now; \
+ return 1; \
+ } \
+ } else { \
+ if (last.roll != now.roll) return 1; \
+ if (last.pitch != now.pitch) return 1; \
+ if (last.yaw != now.yaw) return 1; \
+ } \
+ } while (0)
+
+ #define CROSS_THRESH_XYZ(last, now, thresh) \
+ do { \
+ if (WIIMOTE_IS_FLAG_SET(wm, WIIUSE_ORIENT_THRESH)) { \
+ if ((diff_f(last.x, now.x) >= thresh) || \
+ (diff_f(last.y, now.y) >= thresh) || \
+ (diff_f(last.z, now.z) >= thresh)) \
+ { \
+ last = now; \
+ return 1; \
+ } \
+ } else { \
+ if (last.x != now.x) return 1; \
+ if (last.y != now.y) return 1; \
+ if (last.z != now.z) return 1; \
+ } \
+ } while (0)
+
+ /* ir */
+ if (WIIUSE_USING_IR(wm)) {
+ STATE_CHANGED(wm->lstate.ir_ax, wm->ir.ax);
+ STATE_CHANGED(wm->lstate.ir_ay, wm->ir.ay);
+ STATE_CHANGED(wm->lstate.ir_distance, wm->ir.distance);
+ }
+
+ /* accelerometer */
+ if (WIIUSE_USING_ACC(wm)) {
+ /* raw accelerometer */
+ CROSS_THRESH_XYZ(wm->lstate.accel, wm->accel, wm->accel_threshold);
+
+ /* orientation */
+ CROSS_THRESH(wm->lstate.orient, wm->orient, wm->orient_threshold);
+ }
+
+ /* expansion */
+ switch (wm->exp.type) {
+ case EXP_NUNCHUK:
+ {
+ STATE_CHANGED(wm->lstate.exp_ljs_ang, wm->exp.nunchuk.js.ang);
+ STATE_CHANGED(wm->lstate.exp_ljs_mag, wm->exp.nunchuk.js.mag);
+ STATE_CHANGED(wm->lstate.exp_btns, wm->exp.nunchuk.btns);
+
+ CROSS_THRESH(wm->lstate.exp_orient, wm->exp.nunchuk.orient, wm->exp.nunchuk.orient_threshold);
+ CROSS_THRESH_XYZ(wm->lstate.exp_accel, wm->exp.nunchuk.accel, wm->exp.nunchuk.accel_threshold);
+ break;
+ }
+ case EXP_CLASSIC:
+ {
+ STATE_CHANGED(wm->lstate.exp_ljs_ang, wm->exp.classic.ljs.ang);
+ STATE_CHANGED(wm->lstate.exp_ljs_mag, wm->exp.classic.ljs.mag);
+ STATE_CHANGED(wm->lstate.exp_rjs_ang, wm->exp.classic.rjs.ang);
+ STATE_CHANGED(wm->lstate.exp_rjs_mag, wm->exp.classic.rjs.mag);
+ STATE_CHANGED(wm->lstate.exp_r_shoulder, wm->exp.classic.r_shoulder);
+ STATE_CHANGED(wm->lstate.exp_l_shoulder, wm->exp.classic.l_shoulder);
+ STATE_CHANGED(wm->lstate.exp_btns, wm->exp.classic.btns);
+ break;
+ }
+ case EXP_GUITAR_HERO_3:
+ {
+ STATE_CHANGED(wm->lstate.exp_ljs_ang, wm->exp.gh3.js.ang);
+ STATE_CHANGED(wm->lstate.exp_ljs_mag, wm->exp.gh3.js.mag);
+ STATE_CHANGED(wm->lstate.exp_r_shoulder, wm->exp.gh3.whammy_bar);
+ STATE_CHANGED(wm->lstate.exp_btns, wm->exp.gh3.btns);
+ break;
+ }
+ case EXP_NONE:
+ {
+ break;
+ }
+ }
+
+ STATE_CHANGED(wm->lstate.btns, wm->btns);
+
+ return 0;
+}
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/events.h b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/events.h
new file mode 100644
index 0000000000..5e9b955c55
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/events.h
@@ -0,0 +1,54 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Handles wiimote events.
+ *
+ * The file includes functions that handle the events
+ * that are sent from the wiimote to us.
+ */
+
+#ifndef EVENTS_H_INCLUDED
+#define EVENTS_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void wiiuse_pressed_buttons(struct wiimote_t* wm, byte* msg);
+
+void handshake_expansion(struct wiimote_t* wm, byte* data, unsigned short len);
+void disable_expansion(struct wiimote_t* wm);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // EVENTS_H_INCLUDED
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/guitar_hero_3.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/guitar_hero_3.c
new file mode 100644
index 0000000000..5837dc016f
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/guitar_hero_3.c
@@ -0,0 +1,172 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Guitar Hero 3 expansion device.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#ifdef WIN32
+ #include <Winsock2.h>
+#endif
+
+#include "definitions.h"
+#include "wiiuse_internal.h"
+#include "dynamics.h"
+#include "events.h"
+#include "guitar_hero_3.h"
+
+static void guitar_hero_3_pressed_buttons(struct guitar_hero_3_t* gh3, short now);
+
+/**
+ * @brief Handle the handshake data from the guitar.
+ *
+ * @param cc A pointer to a classic_ctrl_t structure.
+ * @param data The data read in from the device.
+ * @param len The length of the data block, in bytes.
+ *
+ * @return Returns 1 if handshake was successful, 0 if not.
+ */
+int guitar_hero_3_handshake(struct wiimote_t* wm, struct guitar_hero_3_t* gh3, byte* data, unsigned short len) {
+ int i;
+ int offset = 0;
+
+ /*
+ * The good fellows that made the Guitar Hero 3 controller
+ * failed to factory calibrate the devices. There is no
+ * calibration data on the device.
+ */
+
+ gh3->btns = 0;
+ gh3->btns_held = 0;
+ gh3->btns_released = 0;
+ gh3->whammy_bar = 0.0f;
+
+ /* decrypt data */
+ for (i = 0; i < len; ++i)
+ data[i] = (data[i] ^ 0x17) + 0x17;
+
+ if (data[offset] == 0xFF) {
+ /*
+ * Sometimes the data returned here is not correct.
+ * This might happen because the wiimote is lagging
+ * behind our initialization sequence.
+ * To fix this just request the handshake again.
+ *
+ * Other times it's just the first 16 bytes are 0xFF,
+ * but since the next 16 bytes are the same, just use
+ * those.
+ */
+ if (data[offset + 16] == 0xFF) {
+ /* get the calibration data */
+ byte* handshake_buf = malloc(EXP_HANDSHAKE_LEN * sizeof(byte));
+
+ WIIUSE_DEBUG("Guitar Hero 3 handshake appears invalid, trying again.");
+ wiiuse_read_data_cb(wm, handshake_expansion, handshake_buf, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN);
+
+ return 0;
+ } else
+ offset += 16;
+ }
+
+ /* joystick stuff */
+ gh3->js.max.x = GUITAR_HERO_3_JS_MAX_X;
+ gh3->js.min.x = GUITAR_HERO_3_JS_MIN_X;
+ gh3->js.center.x = GUITAR_HERO_3_JS_CENTER_X;
+ gh3->js.max.y = GUITAR_HERO_3_JS_MAX_Y;
+ gh3->js.min.y = GUITAR_HERO_3_JS_MIN_Y;
+ gh3->js.center.y = GUITAR_HERO_3_JS_CENTER_Y;
+
+ /* handshake done */
+ wm->exp.type = EXP_GUITAR_HERO_3;
+
+ #ifdef WIN32
+ wm->timeout = WIIMOTE_DEFAULT_TIMEOUT;
+ #endif
+
+ return 1;
+}
+
+
+/**
+ * @brief The guitar disconnected.
+ *
+ * @param cc A pointer to a classic_ctrl_t structure.
+ */
+void guitar_hero_3_disconnected(struct guitar_hero_3_t* gh3) {
+ memset(gh3, 0, sizeof(struct guitar_hero_3_t));
+}
+
+
+
+/**
+ * @brief Handle guitar event.
+ *
+ * @param cc A pointer to a classic_ctrl_t structure.
+ * @param msg The message specified in the event packet.
+ */
+void guitar_hero_3_event(struct guitar_hero_3_t* gh3, byte* msg) {
+ int i;
+
+ /* decrypt data */
+ for (i = 0; i < 6; ++i)
+ msg[i] = (msg[i] ^ 0x17) + 0x17;
+
+ guitar_hero_3_pressed_buttons(gh3, BIG_ENDIAN_SHORT(*(short*)(msg + 4)));
+
+ /* whammy bar */
+ gh3->whammy_bar = (msg[3] - GUITAR_HERO_3_WHAMMY_BAR_MIN) / (float)(GUITAR_HERO_3_WHAMMY_BAR_MAX - GUITAR_HERO_3_WHAMMY_BAR_MIN);
+
+ /* joy stick */
+ calc_joystick_state(&gh3->js, msg[0], msg[1]);
+}
+
+
+/**
+ * @brief Find what buttons are pressed.
+ *
+ * @param cc A pointer to a classic_ctrl_t structure.
+ * @param msg The message byte specified in the event packet.
+ */
+static void guitar_hero_3_pressed_buttons(struct guitar_hero_3_t* gh3, short now) {
+ /* message is inverted (0 is active, 1 is inactive) */
+ now = ~now & GUITAR_HERO_3_BUTTON_ALL;
+
+ /* pressed now & were pressed, then held */
+ gh3->btns_held = (now & gh3->btns);
+
+ /* were pressed or were held & not pressed now, then released */
+ gh3->btns_released = ((gh3->btns | gh3->btns_held) & ~now);
+
+ /* buttons pressed now */
+ gh3->btns = now;
+}
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/guitar_hero_3.h b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/guitar_hero_3.h
new file mode 100644
index 0000000000..024d603177
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/guitar_hero_3.h
@@ -0,0 +1,62 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Guitar Hero 3 expansion device.
+ */
+
+#ifndef GUITAR_HERO_3_H_INCLUDED
+#define GUITAR_HERO_3_H_INCLUDED
+
+#include "wiiuse_internal.h"
+
+#define GUITAR_HERO_3_JS_MIN_X 0xC5
+#define GUITAR_HERO_3_JS_MAX_X 0xFC
+#define GUITAR_HERO_3_JS_CENTER_X 0xE0
+#define GUITAR_HERO_3_JS_MIN_Y 0xC5
+#define GUITAR_HERO_3_JS_MAX_Y 0xFA
+#define GUITAR_HERO_3_JS_CENTER_Y 0xE0
+#define GUITAR_HERO_3_WHAMMY_BAR_MIN 0xEF
+#define GUITAR_HERO_3_WHAMMY_BAR_MAX 0xFA
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int guitar_hero_3_handshake(struct wiimote_t* wm, struct guitar_hero_3_t* gh3, byte* data, unsigned short len);
+
+void guitar_hero_3_disconnected(struct guitar_hero_3_t* gh3);
+
+void guitar_hero_3_event(struct guitar_hero_3_t* gh3, byte* msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // GUITAR_HERO_3_H_INCLUDED
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io.c
new file mode 100644
index 0000000000..ae420b9ef3
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io.c
@@ -0,0 +1,119 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Handles device I/O (non-OS specific).
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "definitions.h"
+#include "wiiuse_internal.h"
+#include "io.h"
+
+
+ /**
+ * @brief Get initialization data from the wiimote.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param data unused
+ * @param len unused
+ *
+ * When first called for a wiimote_t structure, a request
+ * is sent to the wiimote for initialization information.
+ * This includes factory set accelerometer data.
+ * The handshake will be concluded when the wiimote responds
+ * with this data.
+ */
+void wiiuse_handshake(struct wiimote_t* wm, byte* data, unsigned short len) {
+ if (!wm) return;
+
+ switch (wm->handshake_state) {
+ case 0:
+ {
+ /* send request to wiimote for accelerometer calibration */
+ byte* buf;
+
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
+ wiiuse_set_leds(wm, WIIMOTE_LED_NONE);
+
+ buf = (byte*)malloc(sizeof(byte) * 8);
+ wiiuse_read_data_cb(wm, wiiuse_handshake, buf, WM_MEM_OFFSET_CALIBRATION, 7);
+ wm->handshake_state++;
+
+ wiiuse_set_leds(wm, WIIMOTE_LED_NONE);
+
+ break;
+ }
+ case 1:
+ {
+ struct read_req_t* req = wm->read_req;
+ struct accel_t* accel = &wm->accel_calib;
+
+ /* received read data */
+ accel->cal_zero.x = req->buf[0];
+ accel->cal_zero.y = req->buf[1];
+ accel->cal_zero.z = req->buf[2];
+
+ accel->cal_g.x = req->buf[4] - accel->cal_zero.x;
+ accel->cal_g.y = req->buf[5] - accel->cal_zero.y;
+ accel->cal_g.z = req->buf[6] - accel->cal_zero.z;
+
+ /* done with the buffer */
+ free(req->buf);
+
+ /* handshake is done */
+ WIIUSE_DEBUG("Handshake finished. Calibration: Idle: X=%x Y=%x Z=%x\t+1g: X=%x Y=%x Z=%x",
+ accel->cal_zero.x, accel->cal_zero.y, accel->cal_zero.z,
+ accel->cal_g.x, accel->cal_g.y, accel->cal_g.z);
+
+
+ /* request the status of the wiimote to see if there is an expansion */
+ wiiuse_status(wm);
+
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE);
+ wm->handshake_state++;
+
+ /* now enable IR if it was set before the handshake completed */
+ if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) {
+ WIIUSE_DEBUG("Handshake finished, enabling IR.");
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR);
+ wiiuse_set_ir(wm, 1);
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io.h b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io.h
new file mode 100644
index 0000000000..7a683e4ee5
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io.h
@@ -0,0 +1,56 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Handles device I/O.
+ */
+
+#ifndef CONNECT_H_INCLUDED
+#define CONNECT_H_INCLUDED
+
+#ifndef WIN32
+ #include <bluetooth/bluetooth.h>
+#endif
+
+#include "wiiuse_internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void wiiuse_handshake(struct wiimote_t* wm, byte* data, unsigned short len);
+
+int wiiuse_io_read(struct wiimote_t* wm);
+int wiiuse_io_write(struct wiimote_t* wm, byte* buf, int len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // CONNECT_H_INCLUDED
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io_nix.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io_nix.c
new file mode 100644
index 0000000000..ec4e0e1780
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io_nix.c
@@ -0,0 +1,270 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Handles device I/O for *nix.
+ */
+
+#ifndef WIN32
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#include "definitions.h"
+#include "wiiuse_internal.h"
+#include "io.h"
+
+static int wiiuse_connect_single(struct wiimote_t* wm, char* address);
+
+/**
+ * @brief Find a wiimote or wiimotes.
+ *
+ * @param wm An array of wiimote_t structures.
+ * @param max_wiimotes The number of wiimote structures in \a wm.
+ * @param timeout The number of seconds before the search times out.
+ *
+ * @return The number of wiimotes found.
+ *
+ * @see wiimote_connect()
+ *
+ * This function will only look for wiimote devices. \n
+ * When a device is found the address in the structures will be set. \n
+ * You can then call wiimote_connect() to connect to the found \n
+ * devices.
+ */
+int wiiuse_find(struct wiimote_t** wm, int max_wiimotes, int timeout) {
+ int device_id;
+ int device_sock;
+ int found_devices;
+ int found_wiimotes;
+
+ /* reset all wiimote bluetooth device addresses */
+ for (found_wiimotes = 0; found_wiimotes < max_wiimotes; ++found_wiimotes)
+ wm[found_wiimotes]->bdaddr = *BDADDR_ANY;
+ found_wiimotes = 0;
+
+ /* get the id of the first bluetooth device. */
+ device_id = hci_get_route(NULL);
+ if (device_id < 0) {
+ perror("hci_get_route");
+ return 0;
+ }
+
+ /* create a socket to the device */
+ device_sock = hci_open_dev(device_id);
+ if (device_sock < 0) {
+ perror("hci_open_dev");
+ return 0;
+ }
+
+ inquiry_info scan_info_arr[128];
+ inquiry_info* scan_info = scan_info_arr;
+ memset(&scan_info_arr, 0, sizeof(scan_info_arr));
+
+ /* scan for bluetooth devices for 'timeout' seconds */
+ found_devices = hci_inquiry(device_id, timeout, 128, NULL, &scan_info, IREQ_CACHE_FLUSH);
+ if (found_devices < 0) {
+ perror("hci_inquiry");
+ return 0;
+ }
+
+ WIIUSE_INFO("Found %i bluetooth device(s).", found_devices);
+
+ int i = 0;
+
+ /* display discovered devices */
+ for (; (i < found_devices) && (found_wiimotes < max_wiimotes); ++i) {
+ if ((scan_info[i].dev_class[0] == WM_DEV_CLASS_0) &&
+ (scan_info[i].dev_class[1] == WM_DEV_CLASS_1) &&
+ (scan_info[i].dev_class[2] == WM_DEV_CLASS_2))
+ {
+ /* found a device */
+ ba2str(&scan_info[i].bdaddr, wm[found_wiimotes]->bdaddr_str);
+
+ WIIUSE_INFO("Found wiimote (%s) [id %i].", wm[found_wiimotes]->bdaddr_str, wm[found_wiimotes]->unid);
+
+ wm[found_wiimotes]->bdaddr = scan_info[i].bdaddr;
+ WIIMOTE_ENABLE_STATE(wm[found_wiimotes], WIIMOTE_STATE_DEV_FOUND);
+ ++found_wiimotes;
+ }
+ }
+
+ close(device_sock);
+ return found_wiimotes;
+}
+
+
+/**
+ * @brief Connect to a wiimote or wiimotes once an address is known.
+ *
+ * @param wm An array of wiimote_t structures.
+ * @param wiimotes The number of wiimote structures in \a wm.
+ *
+ * @return The number of wiimotes that successfully connected.
+ *
+ * @see wiiuse_find()
+ * @see wiiuse_connect_single()
+ * @see wiiuse_disconnect()
+ *
+ * Connect to a number of wiimotes when the address is already set
+ * in the wiimote_t structures. These addresses are normally set
+ * by the wiiuse_find() function, but can also be set manually.
+ */
+int wiiuse_connect(struct wiimote_t** wm, int wiimotes) {
+ int connected = 0;
+ int i = 0;
+
+ for (; i < wiimotes; ++i) {
+ if (!WIIMOTE_IS_SET(wm[i], WIIMOTE_STATE_DEV_FOUND))
+ /* if the device address is not set, skip it */
+ continue;
+
+ if (wiiuse_connect_single(wm[i], NULL))
+ ++connected;
+ }
+
+ return connected;
+}
+
+
+/**
+ * @brief Connect to a wiimote with a known address.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param address The address of the device to connect to.
+ * If NULL, use the address in the struct set by wiiuse_find().
+ *
+ * @return 1 on success, 0 on failure
+ */
+static int wiiuse_connect_single(struct wiimote_t* wm, char* address) {
+ struct sockaddr_l2 addr;
+
+ if (!wm || WIIMOTE_IS_CONNECTED(wm))
+ return 0;
+
+ addr.l2_family = AF_BLUETOOTH;
+
+ if (address)
+ /* use provided address */
+ str2ba(address, &addr.l2_bdaddr);
+ else
+ /* use address of device discovered */
+ addr.l2_bdaddr = wm->bdaddr;
+
+ /*
+ * OUTPUT CHANNEL
+ */
+ wm->out_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (wm->out_sock == -1)
+ return 0;
+
+ addr.l2_psm = htobs(WM_OUTPUT_CHANNEL);
+
+ /* connect to wiimote */
+ if (connect(wm->out_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+ perror("connect() output sock");
+ return 0;
+ }
+
+ /*
+ * INPUT CHANNEL
+ */
+ wm->in_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ if (wm->in_sock == -1) {
+ close(wm->out_sock);
+ wm->out_sock = -1;
+ return 0;
+ }
+
+ addr.l2_psm = htobs(WM_INPUT_CHANNEL);
+
+ /* connect to wiimote */
+ if (connect(wm->in_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+ perror("connect() interrupt sock");
+ close(wm->out_sock);
+ wm->out_sock = -1;
+ return 0;
+ }
+
+ WIIUSE_INFO("Connected to wiimote [id %i].", wm->unid);
+
+ /* do the handshake */
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_CONNECTED);
+ wiiuse_handshake(wm, NULL, 0);
+
+ wiiuse_set_report_type(wm);
+
+ return 1;
+}
+
+
+/**
+ * @brief Disconnect a wiimote.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ *
+ * @see wiiuse_connect()
+ *
+ * Note that this will not free the wiimote structure.
+ */
+void wiiuse_disconnect(struct wiimote_t* wm) {
+ if (!wm || WIIMOTE_IS_CONNECTED(wm))
+ return;
+
+ close(wm->out_sock);
+ close(wm->in_sock);
+
+ wm->out_sock = -1;
+ wm->in_sock = -1;
+ wm->event = WIIUSE_NONE;
+
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_CONNECTED);
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
+}
+
+
+int wiiuse_io_read(struct wiimote_t* wm) {
+ /* not used */
+ return 0;
+}
+
+
+int wiiuse_io_write(struct wiimote_t* wm, byte* buf, int len) {
+ return write(wm->out_sock, buf, len);
+}
+
+
+
+#endif /* ifndef WIN32 */
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io_win.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io_win.c
new file mode 100644
index 0000000000..17fac360d3
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/io_win.c
@@ -0,0 +1,247 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Handles device I/O for Windows.
+ */
+
+#ifdef WIN32
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <windows.h>
+#include <hidsdi.h>
+#include <setupapi.h>
+
+#include "definitions.h"
+#include "wiiuse_internal.h"
+#include "io.h"
+
+
+int wiiuse_find(struct wiimote_t** wm, int max_wiimotes, int timeout) {
+ GUID device_id;
+ HANDLE dev;
+ HDEVINFO device_info;
+ int i, index;
+ DWORD len;
+ SP_DEVICE_INTERFACE_DATA device_data;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA detail_data = NULL;
+ HIDD_ATTRIBUTES attr;
+ int found = 0;
+
+ (void) timeout; // unused
+
+ device_data.cbSize = sizeof(device_data);
+ index = 0;
+
+ /* get the device id */
+ HidD_GetHidGuid(&device_id);
+
+ /* get all hid devices connected */
+ device_info = SetupDiGetClassDevs(&device_id, NULL, NULL, (DIGCF_DEVICEINTERFACE | DIGCF_PRESENT));
+
+ for (;; ++index) {
+
+ if (detail_data) {
+ free(detail_data);
+ detail_data = NULL;
+ }
+
+ /* query the next hid device info */
+ if (!SetupDiEnumDeviceInterfaces(device_info, NULL, &device_id, index, &device_data))
+ break;
+
+ /* get the size of the data block required */
+ i = SetupDiGetDeviceInterfaceDetail(device_info, &device_data, NULL, 0, &len, NULL);
+ detail_data = malloc(len);
+ detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+ /* query the data for this device */
+ if (!SetupDiGetDeviceInterfaceDetail(device_info, &device_data, detail_data, len, NULL, NULL))
+ continue;
+
+ /* open the device */
+ dev = CreateFile(detail_data->DevicePath,
+ (GENERIC_READ | GENERIC_WRITE),
+ (FILE_SHARE_READ | FILE_SHARE_WRITE),
+ NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ if (dev == INVALID_HANDLE_VALUE)
+ continue;
+
+ /* get device attributes */
+ attr.Size = sizeof(attr);
+ i = HidD_GetAttributes(dev, &attr);
+
+ if ((attr.VendorID == WM_VENDOR_ID) && (attr.ProductID == WM_PRODUCT_ID)) {
+ /* this is a wiimote */
+ wm[found]->dev_handle = dev;
+
+ wm[found]->hid_overlap.hEvent = CreateEvent(NULL, 1, 1, "");
+ wm[found]->hid_overlap.Offset = 0;
+ wm[found]->hid_overlap.OffsetHigh = 0;
+
+ WIIMOTE_ENABLE_STATE(wm[found], WIIMOTE_STATE_DEV_FOUND);
+ WIIMOTE_ENABLE_STATE(wm[found], WIIMOTE_STATE_CONNECTED);
+
+ /* try to set the output report to see if the device is actually connected */
+ if (!wiiuse_set_report_type(wm[found])) {
+ WIIMOTE_DISABLE_STATE(wm[found], WIIMOTE_STATE_CONNECTED);
+ continue;
+ }
+
+ /* do the handshake */
+ wiiuse_handshake(wm[found], NULL, 0);
+
+ WIIUSE_INFO("Connected to wiimote [id %i].", wm[found]->unid);
+
+ ++found;
+ if (found >= max_wiimotes)
+ break;
+ } else {
+ /* not a wiimote */
+ CloseHandle(dev);
+ }
+ }
+
+ if (detail_data)
+ free(detail_data);
+
+ SetupDiDestroyDeviceInfoList(device_info);
+
+ return found;
+}
+
+
+int wiiuse_connect(struct wiimote_t** wm, int wiimotes) {
+ int connected = 0;
+ int i = 0;
+
+ for (; i < wiimotes; ++i) {
+ if (WIIMOTE_IS_SET(wm[i], WIIMOTE_STATE_CONNECTED))
+ ++connected;
+ }
+
+ return connected;
+}
+
+
+void wiiuse_disconnect(struct wiimote_t* wm) {
+ if (!wm || WIIMOTE_IS_CONNECTED(wm))
+ return;
+
+ CloseHandle(wm->dev_handle);
+ wm->dev_handle = 0;
+
+ ResetEvent(&wm->hid_overlap);
+
+ wm->event = WIIUSE_NONE;
+
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_CONNECTED);
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_HANDSHAKE);
+}
+
+
+int wiiuse_io_read(struct wiimote_t* wm) {
+ DWORD b, r;
+
+ if (!wm || !WIIMOTE_IS_CONNECTED(wm))
+ return 0;
+
+ if (!ReadFile(wm->dev_handle, wm->event_buf, sizeof(wm->event_buf), &b, &wm->hid_overlap)) {
+ /* partial read */
+ b = GetLastError();
+
+ if ((b == ERROR_HANDLE_EOF) || (b == ERROR_DEVICE_NOT_CONNECTED)) {
+ /* remote disconnect */
+ wiiuse_disconnected(wm);
+ return 0;
+ }
+
+ r = WaitForSingleObject(wm->hid_overlap.hEvent, wm->timeout);
+ if (r == WAIT_TIMEOUT) {
+ /* timeout - cancel and continue */
+
+ if (*wm->event_buf)
+ WIIUSE_WARNING("Packet ignored. This may indicate a problem (timeout is %i ms).", wm->timeout);
+
+ CancelIo(wm->dev_handle);
+ ResetEvent(wm->hid_overlap.hEvent);
+ return 0;
+ } else if (r == WAIT_FAILED) {
+ WIIUSE_WARNING("A wait error occured on reading from wiimote %i.", wm->unid);
+ return 0;
+ }
+
+ if (!GetOverlappedResult(wm->dev_handle, &wm->hid_overlap, &b, 0))
+ return 0;
+ }
+
+ ResetEvent(wm->hid_overlap.hEvent);
+ return 1;
+}
+
+
+int wiiuse_io_write(struct wiimote_t* wm, byte* buf, int len) {
+ DWORD bytes;
+ int i;
+
+ if (!wm || !WIIMOTE_IS_CONNECTED(wm))
+ return 0;
+
+ switch (wm->stack) {
+ case WIIUSE_STACK_UNKNOWN:
+ {
+ /* try to auto-detect the stack type */
+ if (i = WriteFile(wm->dev_handle, buf, 22, &bytes, &wm->hid_overlap)) {
+ /* bluesoleil will always return 1 here, even if it's not connected */
+ wm->stack = WIIUSE_STACK_BLUESOLEIL;
+ return i;
+ }
+
+ if (i = HidD_SetOutputReport(wm->dev_handle, buf, len)) {
+ wm->stack = WIIUSE_STACK_MS;
+ return i;
+ }
+
+ WIIUSE_ERROR("Unable to determine bluetooth stack type.");
+ return 0;
+ }
+
+ case WIIUSE_STACK_MS:
+ return HidD_SetOutputReport(wm->dev_handle, buf, len);
+
+ case WIIUSE_STACK_BLUESOLEIL:
+ return WriteFile(wm->dev_handle, buf, 22, &bytes, &wm->hid_overlap);
+ }
+
+ return 0;
+}
+
+#endif /* ifdef WIN32 */
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/ir.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/ir.c
new file mode 100644
index 0000000000..7a9bb68289
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/ir.c
@@ -0,0 +1,748 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Handles IR data.
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+#ifndef WIN32
+ #include <unistd.h>
+#endif
+
+#include "definitions.h"
+#include "wiiuse_internal.h"
+#include "ir.h"
+
+static int get_ir_sens(struct wiimote_t* wm, char** block1, char** block2);
+static void interpret_ir_data(struct wiimote_t* wm);
+static void fix_rotated_ir_dots(struct ir_dot_t* dot, float ang);
+static void get_ir_dot_avg(struct ir_dot_t* dot, int* x, int* y);
+static void reorder_ir_dots(struct ir_dot_t* dot);
+static float ir_distance(struct ir_dot_t* dot);
+static int ir_correct_for_bounds(int* x, int* y, enum aspect_t aspect, int offset_x, int offset_y);
+static void ir_convert_to_vres(int* x, int* y, enum aspect_t aspect, int vx, int vy);
+
+
+/**
+ * @brief Set if the wiimote should track IR targets.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param status 1 to enable, 0 to disable.
+ */
+void wiiuse_set_ir(struct wiimote_t* wm, int status) {
+ byte buf;
+ char* block1 = NULL;
+ char* block2 = NULL;
+ int ir_level;
+
+ if (!wm)
+ return;
+
+ /*
+ * Wait for the handshake to finish first.
+ * When it handshake finishes and sees that
+ * IR is enabled, it will call this function
+ * again to actually enable IR.
+ */
+ if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_HANDSHAKE_COMPLETE)) {
+ WIIUSE_DEBUG("Tried to enable IR, will wait until handshake finishes.");
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR);
+ return;
+ }
+
+ /*
+ * Check to make sure a sensitivity setting is selected.
+ */
+ ir_level = get_ir_sens(wm, &block1, &block2);
+ if (!ir_level) {
+ WIIUSE_ERROR("No IR sensitivity setting selected.");
+ return;
+ }
+
+ if (status) {
+ /* if already enabled then stop */
+ if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR))
+ return;
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR);
+ } else {
+ /* if already disabled then stop */
+ if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR))
+ return;
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR);
+ }
+
+ /* set camera 1 and 2 */
+ buf = (status ? 0x04 : 0x00);
+ wiiuse_send(wm, WM_CMD_IR, &buf, 1);
+ wiiuse_send(wm, WM_CMD_IR_2, &buf, 1);
+
+ if (!status) {
+ WIIUSE_DEBUG("Disabled IR cameras for wiimote id %i.", wm->unid);
+ wiiuse_set_report_type(wm);
+ return;
+ }
+
+ /* enable IR, set sensitivity */
+ buf = 0x08;
+ wiiuse_write_data(wm, WM_REG_IR, &buf, 1);
+
+ /* wait for the wiimote to catch up */
+ #ifndef WIN32
+ usleep(50000);
+ #else
+ Sleep(50);
+ #endif
+
+ /* write sensitivity blocks */
+ wiiuse_write_data(wm, WM_REG_IR_BLOCK1, (byte*)block1, 9);
+ wiiuse_write_data(wm, WM_REG_IR_BLOCK2, (byte*)block2, 2);
+
+ /* set the IR mode */
+ if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP))
+ buf = WM_IR_TYPE_BASIC;
+ else
+ buf = WM_IR_TYPE_EXTENDED;
+ wiiuse_write_data(wm, WM_REG_IR_MODENUM, &buf, 1);
+
+ #ifndef WIN32
+ usleep(50000);
+ #else
+ Sleep(50);
+ #endif
+
+ /* set the wiimote report type */
+ wiiuse_set_report_type(wm);
+
+ WIIUSE_DEBUG("Enabled IR camera for wiimote id %i (sensitivity level %i).", wm->unid, ir_level);
+}
+
+
+/**
+ * @brief Get the IR sensitivity settings.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param block1 [out] Pointer to where block1 will be set.
+ * @param block2 [out] Pointer to where block2 will be set.
+ *
+ * @return Returns the sensitivity level.
+ */
+static int get_ir_sens(struct wiimote_t* wm, char** block1, char** block2) {
+ if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL1)) {
+ *block1 = WM_IR_BLOCK1_LEVEL1;
+ *block2 = WM_IR_BLOCK2_LEVEL1;
+ return 1;
+ } else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL2)) {
+ *block1 = WM_IR_BLOCK1_LEVEL2;
+ *block2 = WM_IR_BLOCK2_LEVEL2;
+ return 2;
+ } else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL3)) {
+ *block1 = WM_IR_BLOCK1_LEVEL3;
+ *block2 = WM_IR_BLOCK2_LEVEL3;
+ return 3;
+ } else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL4)) {
+ *block1 = WM_IR_BLOCK1_LEVEL4;
+ *block2 = WM_IR_BLOCK2_LEVEL4;
+ return 4;
+ } else if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR_SENS_LVL5)) {
+ *block1 = WM_IR_BLOCK1_LEVEL5;
+ *block2 = WM_IR_BLOCK2_LEVEL5;
+ return 5;
+ }
+
+ *block1 = NULL;
+ *block2 = NULL;
+ return 0;
+}
+
+
+/**
+ * @brief Set the virtual screen resolution for IR tracking.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param status 1 to enable, 0 to disable.
+ */
+void wiiuse_set_ir_vres(struct wiimote_t* wm, unsigned int x, unsigned int y) {
+ if (!wm) return;
+
+ wm->ir.vres[0] = (x-1);
+ wm->ir.vres[1] = (y-1);
+}
+
+
+/**
+ * @brief Set the XY position for the IR cursor.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ */
+void wiiuse_set_ir_position(struct wiimote_t* wm, enum ir_position_t pos) {
+ if (!wm) return;
+
+ wm->ir.pos = pos;
+
+ switch (pos) {
+
+ case WIIUSE_IR_ABOVE:
+ wm->ir.offset[0] = 0;
+
+ if (wm->ir.aspect == WIIUSE_ASPECT_16_9)
+ wm->ir.offset[1] = WM_ASPECT_16_9_Y/2 - 70;
+ else if (wm->ir.aspect == WIIUSE_ASPECT_4_3)
+ wm->ir.offset[1] = WM_ASPECT_4_3_Y/2 - 100;
+
+ return;
+
+ case WIIUSE_IR_BELOW:
+ wm->ir.offset[0] = 0;
+
+ if (wm->ir.aspect == WIIUSE_ASPECT_16_9)
+ wm->ir.offset[1] = -WM_ASPECT_16_9_Y/2 + 100;
+ else if (wm->ir.aspect == WIIUSE_ASPECT_4_3)
+ wm->ir.offset[1] = -WM_ASPECT_4_3_Y/2 + 70;
+
+ return;
+
+ default:
+ return;
+ };
+}
+
+
+/**
+ * @brief Set the aspect ratio of the TV/monitor.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param aspect Either WIIUSE_ASPECT_16_9 or WIIUSE_ASPECT_4_3
+ */
+void wiiuse_set_aspect_ratio(struct wiimote_t* wm, enum aspect_t aspect) {
+ if (!wm) return;
+
+ wm->ir.aspect = aspect;
+
+ if (aspect == WIIUSE_ASPECT_4_3) {
+ wm->ir.vres[0] = WM_ASPECT_4_3_X;
+ wm->ir.vres[1] = WM_ASPECT_4_3_Y;
+ } else {
+ wm->ir.vres[0] = WM_ASPECT_16_9_X;
+ wm->ir.vres[1] = WM_ASPECT_16_9_Y;
+ }
+
+ /* reset the position offsets */
+ wiiuse_set_ir_position(wm, wm->ir.pos);
+}
+
+
+/**
+ * @brief Set the IR sensitivity.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param level 1-5, same as Wii system sensitivity setting.
+ *
+ * If the level is < 1, then level will be set to 1.
+ * If the level is > 5, then level will be set to 5.
+ */
+void wiiuse_set_ir_sensitivity(struct wiimote_t* wm, int level) {
+ char* block1 = NULL;
+ char* block2 = NULL;
+
+ if (!wm) return;
+
+ if (level > 5) level = 5;
+ if (level < 1) level = 1;
+
+ WIIMOTE_DISABLE_STATE(wm, (WIIMOTE_STATE_IR_SENS_LVL1 |
+ WIIMOTE_STATE_IR_SENS_LVL2 |
+ WIIMOTE_STATE_IR_SENS_LVL3 |
+ WIIMOTE_STATE_IR_SENS_LVL4 |
+ WIIMOTE_STATE_IR_SENS_LVL5));
+
+ switch (level) {
+ case 1:
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL1);
+ break;
+ case 2:
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL2);
+ break;
+ case 3:
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL3);
+ break;
+ case 4:
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL4);
+ break;
+ case 5:
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_IR_SENS_LVL5);
+ break;
+ default:
+ return;
+ }
+
+ /* set the new sensitivity */
+ get_ir_sens(wm, &block1, &block2);
+
+ wiiuse_write_data(wm, WM_REG_IR_BLOCK1, (byte*)block1, 9);
+ wiiuse_write_data(wm, WM_REG_IR_BLOCK2, (byte*)block2, 2);
+
+ WIIUSE_DEBUG("Set IR sensitivity to level %i (unid %i)", level, wm->unid);
+}
+
+
+/**
+ * @brief Calculate the data from the IR spots. Basic IR mode.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param data Data returned by the wiimote for the IR spots.
+ */
+void calculate_basic_ir(struct wiimote_t* wm, byte* data) {
+ struct ir_dot_t* dot = wm->ir.dot;
+ int i;
+
+ dot[0].rx = 1023 - (data[0] | ((data[2] & 0x30) << 4));
+ dot[0].ry = data[1] | ((data[2] & 0xC0) << 2);
+
+ dot[1].rx = 1023 - (data[3] | ((data[2] & 0x03) << 8));
+ dot[1].ry = data[4] | ((data[2] & 0x0C) << 6);
+
+ dot[2].rx = 1023 - (data[5] | ((data[7] & 0x30) << 4));
+ dot[2].ry = data[6] | ((data[7] & 0xC0) << 2);
+
+ dot[3].rx = 1023 - (data[8] | ((data[7] & 0x03) << 8));
+ dot[3].ry = data[9] | ((data[7] & 0x0C) << 6);
+
+ /* set each IR spot to visible if spot is in range */
+ for (i = 0; i < 4; ++i) {
+ if (dot[i].ry == 1023)
+ dot[i].visible = 0;
+ else {
+ dot[i].visible = 1;
+ dot[i].size = 0; /* since we don't know the size, set it as 0 */
+ }
+ }
+
+ interpret_ir_data(wm);
+}
+
+
+/**
+ * @brief Calculate the data from the IR spots. Extended IR mode.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param data Data returned by the wiimote for the IR spots.
+ */
+void calculate_extended_ir(struct wiimote_t* wm, byte* data) {
+ struct ir_dot_t* dot = wm->ir.dot;
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ dot[i].rx = 1023 - (data[3*i] | ((data[(3*i)+2] & 0x30) << 4));
+ dot[i].ry = data[(3*i)+1] | ((data[(3*i)+2] & 0xC0) << 2);
+
+ dot[i].size = data[(3*i)+2] & 0x0F;
+
+ /* if in range set to visible */
+ if (dot[i].ry == 1023)
+ dot[i].visible = 0;
+ else
+ dot[i].visible = 1;
+ }
+
+ interpret_ir_data(wm);
+}
+
+
+/**
+ * @brief Interpret IR data into more user friendly variables.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ */
+static void interpret_ir_data(struct wiimote_t* wm) {
+ struct ir_dot_t* dot = wm->ir.dot;
+ int i;
+ float roll = 0.0f;
+ int last_num_dots = wm->ir.num_dots;
+
+ if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_ACC))
+ roll = wm->orient.roll;
+
+ /* count visible dots */
+ wm->ir.num_dots = 0;
+ for (i = 0; i < 4; ++i) {
+ if (dot[i].visible)
+ wm->ir.num_dots++;
+ }
+
+ switch (wm->ir.num_dots) {
+ case 0:
+ {
+ wm->ir.state = 0;
+
+ /* reset the dot ordering */
+ for (i = 0; i < 4; ++i)
+ dot[i].order = 0;
+
+ wm->ir.x = 0;
+ wm->ir.y = 0;
+ wm->ir.z = 0.0f;
+
+ return;
+ }
+ case 1:
+ {
+ fix_rotated_ir_dots(wm->ir.dot, roll);
+
+ if (wm->ir.state < 2) {
+ /*
+ * Only 1 known dot, so use just that.
+ */
+ for (i = 0; i < 4; ++i) {
+ if (dot[i].visible) {
+ wm->ir.x = dot[i].x;
+ wm->ir.y = dot[i].y;
+
+ wm->ir.ax = wm->ir.x;
+ wm->ir.ay = wm->ir.y;
+
+ /* can't calculate yaw because we don't have the distance */
+ //wm->orient.yaw = calc_yaw(&wm->ir);
+
+ ir_convert_to_vres(&wm->ir.x, &wm->ir.y, wm->ir.aspect, wm->ir.vres[0], wm->ir.vres[1]);
+ break;
+ }
+ }
+ } else {
+ /*
+ * Only see 1 dot but know theres 2.
+ * Try to estimate where the other one
+ * should be and use that.
+ */
+ for (i = 0; i < 4; ++i) {
+ if (dot[i].visible) {
+ int ox = 0;
+ int x, y;
+
+ if (dot[i].order == 1)
+ /* visible is the left dot - estimate where the right is */
+ ox = dot[i].x + wm->ir.distance;
+ else if (dot[i].order == 2)
+ /* visible is the right dot - estimate where the left is */
+ ox = dot[i].x - wm->ir.distance;
+
+ x = ((signed int)dot[i].x + ox) / 2;
+ y = dot[i].y;
+
+ wm->ir.ax = x;
+ wm->ir.ay = y;
+ wm->orient.yaw = calc_yaw(&wm->ir);
+
+ if (ir_correct_for_bounds(&x, &y, wm->ir.aspect, wm->ir.offset[0], wm->ir.offset[1])) {
+ ir_convert_to_vres(&x, &y, wm->ir.aspect, wm->ir.vres[0], wm->ir.vres[1]);
+ wm->ir.x = x;
+ wm->ir.y = y;
+ }
+
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ case 2:
+ case 3:
+ case 4:
+ {
+ /*
+ * Two (or more) dots known and seen.
+ * Average them together to estimate the true location.
+ */
+ int x, y;
+ wm->ir.state = 2;
+
+ fix_rotated_ir_dots(wm->ir.dot, roll);
+
+ /* if there is at least 1 new dot, reorder them all */
+ if (wm->ir.num_dots > last_num_dots) {
+ reorder_ir_dots(dot);
+ wm->ir.x = 0;
+ wm->ir.y = 0;
+ }
+
+ wm->ir.distance = ir_distance(dot);
+ wm->ir.z = 1023 - wm->ir.distance;
+
+ get_ir_dot_avg(wm->ir.dot, &x, &y);
+
+ wm->ir.ax = x;
+ wm->ir.ay = y;
+ wm->orient.yaw = calc_yaw(&wm->ir);
+
+ if (ir_correct_for_bounds(&x, &y, wm->ir.aspect, wm->ir.offset[0], wm->ir.offset[1])) {
+ ir_convert_to_vres(&x, &y, wm->ir.aspect, wm->ir.vres[0], wm->ir.vres[1]);
+ wm->ir.x = x;
+ wm->ir.y = y;
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ #ifdef WITH_WIIUSE_DEBUG
+ {
+ int ir_level;
+ WIIUSE_GET_IR_SENSITIVITY(wm, &ir_level);
+ WIIUSE_DEBUG("IR sensitivity: %i", ir_level);
+ WIIUSE_DEBUG("IR visible dots: %i", wm->ir.num_dots);
+ for (i = 0; i < 4; ++i)
+ if (dot[i].visible)
+ WIIUSE_DEBUG("IR[%i][order %i] (%.3i, %.3i) -> (%.3i, %.3i)", i, dot[i].order, dot[i].rx, dot[i].ry, dot[i].x, dot[i].y);
+ WIIUSE_DEBUG("IR[absolute]: (%i, %i)", wm->ir.x, wm->ir.y);
+ }
+ #endif
+}
+
+
+
+/**
+ * @brief Fix the rotation of the IR dots.
+ *
+ * @param dot An array of 4 ir_dot_t objects.
+ * @param ang The roll angle to correct by (-180, 180)
+ *
+ * If there is roll then the dots are rotated
+ * around the origin and give a false cursor
+ * position. Correct for the roll.
+ *
+ * If the accelerometer is off then obviously
+ * this will not do anything and the cursor
+ * position may be inaccurate.
+ */
+static void fix_rotated_ir_dots(struct ir_dot_t* dot, float ang) {
+ float s, c;
+ int x, y;
+ int i;
+
+ if (!ang) {
+ for (i = 0; i < 4; ++i) {
+ dot[i].x = dot[i].rx;
+ dot[i].y = dot[i].ry;
+ }
+ return;
+ }
+
+ s = sin(DEGREE_TO_RAD(ang));
+ c = cos(DEGREE_TO_RAD(ang));
+
+ /*
+ * [ cos(theta) -sin(theta) ][ ir->rx ]
+ * [ sin(theta) cos(theta) ][ ir->ry ]
+ */
+
+ for (i = 0; i < 4; ++i) {
+ if (!dot[i].visible)
+ continue;
+
+ x = dot[i].rx - (1024/2);
+ y = dot[i].ry - (768/2);
+
+ dot[i].x = (c * x) + (-s * y);
+ dot[i].y = (s * x) + (c * y);
+
+ dot[i].x += (1024/2);
+ dot[i].y += (768/2);
+ }
+}
+
+
+/**
+ * @brief Average IR dots.
+ *
+ * @param dot An array of 4 ir_dot_t objects.
+ * @param x [out] Average X
+ * @param y [out] Average Y
+ */
+static void get_ir_dot_avg(struct ir_dot_t* dot, int* x, int* y) {
+ int vis = 0, i = 0;
+
+ *x = 0;
+ *y = 0;
+
+ for (; i < 4; ++i) {
+ if (dot[i].visible) {
+ *x += dot[i].x;
+ *y += dot[i].y;
+ ++vis;
+ }
+ }
+
+ *x /= vis;
+ *y /= vis;
+}
+
+
+/**
+ * @brief Reorder the IR dots.
+ *
+ * @param dot An array of 4 ir_dot_t objects.
+ */
+static void reorder_ir_dots(struct ir_dot_t* dot) {
+ int i, j, order;
+
+ /* reset the dot ordering */
+ for (i = 0; i < 4; ++i)
+ dot[i].order = 0;
+
+ for (order = 1; order < 5; ++order) {
+ i = 0;
+
+ for (; !dot[i].visible || dot[i].order; ++i)
+ if (i > 4)
+ return;
+
+ for (j = 0; j < 4; ++j) {
+ if (dot[j].visible && !dot[j].order && (dot[j].x < dot[i].x))
+ i = j;
+ }
+
+ dot[i].order = order;
+ }
+}
+
+
+/**
+ * @brief Calculate the distance between the first 2 visible IR dots.
+ *
+ * @param dot An array of 4 ir_dot_t objects.
+ */
+static float ir_distance(struct ir_dot_t* dot) {
+ int i1, i2;
+ int xd, yd;
+
+ for (i1 = 0; i1 < 4; ++i1)
+ if (dot[i1].visible)
+ break;
+ if (i1 == 4)
+ return 0.0f;
+
+ for (i2 = i1+1; i2 < 4; ++i2)
+ if (dot[i2].visible)
+ break;
+ if (i2 == 4)
+ return 0.0f;
+
+ xd = dot[i2].x - dot[i1].x;
+ yd = dot[i2].y - dot[i1].y;
+
+ return sqrt(xd*xd + yd*yd);
+}
+
+
+/**
+ * @brief Correct for the IR bounding box.
+ *
+ * @param x [out] The current X, it will be updated if valid.
+ * @param y [out] The current Y, it will be updated if valid.
+ * @param aspect Aspect ratio of the screen.
+ * @param offset_x The X offset of the bounding box.
+ * @param offset_y The Y offset of the bounding box.
+ *
+ * @return Returns 1 if the point is valid and was updated.
+ *
+ * Nintendo was smart with this bit. They sacrifice a little
+ * precision for a big increase in usability.
+ */
+static int ir_correct_for_bounds(int* x, int* y, enum aspect_t aspect, int offset_x, int offset_y) {
+ int x0, y0;
+ int xs, ys;
+
+ if (aspect == WIIUSE_ASPECT_16_9) {
+ xs = WM_ASPECT_16_9_X;
+ ys = WM_ASPECT_16_9_Y;
+ } else {
+ xs = WM_ASPECT_4_3_X;
+ ys = WM_ASPECT_4_3_Y;
+ }
+
+ x0 = ((1024 - xs) / 2) + offset_x;
+ y0 = ((768 - ys) / 2) + offset_y;
+
+ if ((*x >= x0)
+ && (*x <= (x0 + xs))
+ && (*y >= y0)
+ && (*y <= (y0 + ys)))
+ {
+ *x -= offset_x;
+ *y -= offset_y;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * @brief Interpolate the point to the user defined virtual screen resolution.
+ */
+static void ir_convert_to_vres(int* x, int* y, enum aspect_t aspect, int vx, int vy) {
+ int xs, ys;
+
+ if (aspect == WIIUSE_ASPECT_16_9) {
+ xs = WM_ASPECT_16_9_X;
+ ys = WM_ASPECT_16_9_Y;
+ } else {
+ xs = WM_ASPECT_4_3_X;
+ ys = WM_ASPECT_4_3_Y;
+ }
+
+ *x -= ((1024-xs)/2);
+ *y -= ((768-ys)/2);
+
+ *x = (*x / (float)xs) * vx;
+ *y = (*y / (float)ys) * vy;
+}
+
+
+/**
+ * @brief Calculate yaw given the IR data.
+ *
+ * @param ir IR data structure.
+ */
+float calc_yaw(struct ir_t* ir) {
+ float x;
+
+ x = ir->ax - 512;
+ x = x * (ir->z / 1024.0f);
+
+ return RAD_TO_DEGREE( atanf(x / ir->z) );
+}
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/ir.h b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/ir.h
new file mode 100644
index 0000000000..9082492987
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/ir.h
@@ -0,0 +1,56 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Handles IR data.
+ */
+
+#ifndef IR_H_INCLUDED
+#define IR_H_INCLUDED
+
+#include "wiiuse_internal.h"
+
+#define WII_VRES_X 560
+#define WII_VRES_Y 340
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void calculate_basic_ir(struct wiimote_t* wm, byte* data);
+void calculate_extended_ir(struct wiimote_t* wm, byte* data);
+float calc_yaw(struct ir_t* ir);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // IR_H_INCLUDED
+
+
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.dsp b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.dsp
new file mode 100644
index 0000000000..dd66ae7078
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.dsp
@@ -0,0 +1,191 @@
+# Microsoft Developer Studio Project File - Name="wiiuse" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=wiiuse - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "wiiuse.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "wiiuse.mak" CFG="wiiuse - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "wiiuse - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "wiiuse - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "wiiuse - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WIIUSE_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WIIUSE_EXPORTS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Ws2_32.lib hid.lib setupapi.lib /nologo /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "wiiuse - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WIIUSE_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WIIUSE_EXPORTS" /D "WITH_WIIUSE_DEBUG" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib Ws2_32.lib hid.lib setupapi.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "wiiuse - Win32 Release"
+# Name "wiiuse - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\classic.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\dynamics.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\events.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\guitar_hero_3.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\io.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\io_nix.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\io_win.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\ir.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\nunchuk.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\wiiuse.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\classic.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\definitions.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\dynamics.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\events.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\guitar_hero_3.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\io.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\ir.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\nunchuk.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\os.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\wiiuse.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\wiiuse_internal.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=..\..\CHANGELOG
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.dsw b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.dsw
new file mode 100644
index 0000000000..5d662bf0ea
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "wiiuse"=".\wiiuse.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.opt b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.opt
new file mode 100644
index 0000000000..b6ebe4c479
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/msvc/wiiuse.opt
Binary files differ
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/nunchuk.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/nunchuk.c
new file mode 100644
index 0000000000..48dbe6a089
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/nunchuk.c
@@ -0,0 +1,210 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Nunchuk expansion device.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "definitions.h"
+#include "wiiuse_internal.h"
+#include "dynamics.h"
+#include "events.h"
+#include "nunchuk.h"
+
+static void nunchuk_pressed_buttons(struct nunchuk_t* nc, byte now);
+
+/**
+ * @brief Handle the handshake data from the nunchuk.
+ *
+ * @param nc A pointer to a nunchuk_t structure.
+ * @param data The data read in from the device.
+ * @param len The length of the data block, in bytes.
+ *
+ * @return Returns 1 if handshake was successful, 0 if not.
+ */
+int nunchuk_handshake(struct wiimote_t* wm, struct nunchuk_t* nc, byte* data, unsigned short len) {
+ int i;
+ int offset = 0;
+
+ nc->btns = 0;
+ nc->btns_held = 0;
+ nc->btns_released = 0;
+
+ /* set the smoothing to the same as the wiimote */
+ nc->flags = &wm->flags;
+ nc->accel_calib.st_alpha = wm->accel_calib.st_alpha;
+
+ /* decrypt data */
+ for (i = 0; i < len; ++i)
+ data[i] = (data[i] ^ 0x17) + 0x17;
+
+ if (data[offset] == 0xFF) {
+ /*
+ * Sometimes the data returned here is not correct.
+ * This might happen because the wiimote is lagging
+ * behind our initialization sequence.
+ * To fix this just request the handshake again.
+ *
+ * Other times it's just the first 16 bytes are 0xFF,
+ * but since the next 16 bytes are the same, just use
+ * those.
+ */
+ if (data[offset + 16] == 0xFF) {
+ /* get the calibration data */
+ byte* handshake_buf = malloc(EXP_HANDSHAKE_LEN * sizeof(byte));
+
+ WIIUSE_DEBUG("Nunchuk handshake appears invalid, trying again.");
+ wiiuse_read_data_cb(wm, handshake_expansion, handshake_buf, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN);
+
+ return 0;
+ } else
+ offset += 16;
+ }
+
+ nc->accel_calib.cal_zero.x = data[offset + 0];
+ nc->accel_calib.cal_zero.y = data[offset + 1];
+ nc->accel_calib.cal_zero.z = data[offset + 2];
+ nc->accel_calib.cal_g.x = data[offset + 4];
+ nc->accel_calib.cal_g.y = data[offset + 5];
+ nc->accel_calib.cal_g.z = data[offset + 6];
+ nc->js.max.x = data[offset + 8];
+ nc->js.min.x = data[offset + 9];
+ nc->js.center.x = data[offset + 10];
+ nc->js.max.y = data[offset + 11];
+ nc->js.min.y = data[offset + 12];
+ nc->js.center.y = data[offset + 13];
+
+ /* default the thresholds to the same as the wiimote */
+ nc->orient_threshold = wm->orient_threshold;
+ nc->accel_threshold = wm->accel_threshold;
+
+ /* handshake done */
+ wm->exp.type = EXP_NUNCHUK;
+
+ #ifdef WIN32
+ wm->timeout = WIIMOTE_DEFAULT_TIMEOUT;
+ #endif
+
+ return 1;
+}
+
+
+/**
+ * @brief The nunchuk disconnected.
+ *
+ * @param nc A pointer to a nunchuk_t structure.
+ */
+void nunchuk_disconnected(struct nunchuk_t* nc) {
+ memset(nc, 0, sizeof(struct nunchuk_t));
+}
+
+
+
+/**
+ * @brief Handle nunchuk event.
+ *
+ * @param nc A pointer to a nunchuk_t structure.
+ * @param msg The message specified in the event packet.
+ */
+void nunchuk_event(struct nunchuk_t* nc, byte* msg) {
+ int i;
+
+ /* decrypt data */
+ for (i = 0; i < 6; ++i)
+ msg[i] = (msg[i] ^ 0x17) + 0x17;
+
+ /* get button states */
+ nunchuk_pressed_buttons(nc, msg[5]);
+
+ /* calculate joystick state */
+ calc_joystick_state(&nc->js, msg[0], msg[1]);
+
+ /* calculate orientation */
+ nc->accel.x = msg[2];
+ nc->accel.y = msg[3];
+ nc->accel.z = msg[4];
+
+ calculate_orientation(&nc->accel_calib, &nc->accel, &nc->orient, NUNCHUK_IS_FLAG_SET(nc, WIIUSE_SMOOTHING));
+ calculate_gforce(&nc->accel_calib, &nc->accel, &nc->gforce);
+}
+
+
+/**
+ * @brief Find what buttons are pressed.
+ *
+ * @param nc Pointer to a nunchuk_t structure.
+ * @param msg The message byte specified in the event packet.
+ */
+static void nunchuk_pressed_buttons(struct nunchuk_t* nc, byte now) {
+ /* message is inverted (0 is active, 1 is inactive) */
+ now = ~now & NUNCHUK_BUTTON_ALL;
+
+ /* pressed now & were pressed, then held */
+ nc->btns_held = (now & nc->btns);
+
+ /* were pressed or were held & not pressed now, then released */
+ nc->btns_released = ((nc->btns | nc->btns_held) & ~now);
+
+ /* buttons pressed now */
+ nc->btns = now;
+}
+
+
+/**
+ * @brief Set the orientation event threshold for the nunchuk.
+ *
+ * @param wm Pointer to a wiimote_t structure with a nunchuk attached.
+ * @param threshold The decimal place that should be considered a significant change.
+ *
+ * See wiiuse_set_orient_threshold() for details.
+ */
+void wiiuse_set_nunchuk_orient_threshold(struct wiimote_t* wm, float threshold) {
+ if (!wm) return;
+
+ wm->exp.nunchuk.orient_threshold = threshold;
+}
+
+
+/**
+ * @brief Set the accelerometer event threshold for the nunchuk.
+ *
+ * @param wm Pointer to a wiimote_t structure with a nunchuk attached.
+ * @param threshold The decimal place that should be considered a significant change.
+ *
+ * See wiiuse_set_orient_threshold() for details.
+ */
+void wiiuse_set_nunchuk_accel_threshold(struct wiimote_t* wm, int threshold) {
+ if (!wm) return;
+
+ wm->exp.nunchuk.accel_threshold = threshold;
+}
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/nunchuk.h b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/nunchuk.h
new file mode 100644
index 0000000000..f036073d64
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/nunchuk.h
@@ -0,0 +1,53 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief Nunchuk expansion device.
+ */
+
+#ifndef NUNCHUK_H_INCLUDED
+#define NUNCHUK_H_INCLUDED
+
+#include "wiiuse_internal.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int nunchuk_handshake(struct wiimote_t* wm, struct nunchuk_t* nc, byte* data, unsigned short len);
+
+void nunchuk_disconnected(struct nunchuk_t* nc);
+
+void nunchuk_event(struct nunchuk_t* nc, byte* msg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // NUNCHUK_H_INCLUDED
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/os.h b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/os.h
new file mode 100644
index 0000000000..53b80f557c
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/os.h
@@ -0,0 +1,56 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+
+/**
+ * @file
+ * @brief Operating system related definitions.
+ *
+ * This file is an attempt to separate operating system
+ * dependent functions and choose what should be used
+ * at compile time.
+ */
+
+#ifndef OS_H_INCLUDED
+#define OS_H_INCLUDED
+
+#ifdef WIN32
+ /* windows */
+ #define isnan(x) _isnan(x)
+ #define isinf(x) !_finite(x)
+
+ /* disable warnings I don't care about */
+ #pragma warning(disable:4244) /* possible loss of data conversion */
+ #pragma warning(disable:4273) /* inconsistent dll linkage */
+ #pragma warning(disable:4217)
+#else
+ /* nix */
+#endif
+
+
+#endif // OS_H_INCLUDED
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse.c b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse.c
new file mode 100644
index 0000000000..8d4b763864
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse.c
@@ -0,0 +1,764 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief General wiimote operations.
+ *
+ * The file includes functions that handle general
+ * tasks. Most of these are functions that are part
+ * of the API.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef WIN32
+ #include <unistd.h>
+#else
+ #include <Winsock2.h>
+#endif
+
+#include "definitions.h"
+#include "wiiuse_internal.h"
+#include "events.h"
+#include "io.h"
+
+static int g_banner = 0;
+
+/**
+ * @breif Returns the version of the library.
+ */
+const char* wiiuse_version() {
+ return WIIUSE_VERSION;
+}
+
+
+/**
+ * @brief Clean up wiimote_t array created by wiiuse_init()
+ */
+void wiiuse_cleanup(struct wiimote_t** wm, int wiimotes) {
+ int i = 0;
+
+ if (!wm)
+ return;
+
+ WIIUSE_INFO("wiiuse clean up...");
+
+ for (; i < wiimotes; ++i) {
+ wiiuse_disconnect(wm[i]);
+ free(wm[i]);
+ }
+
+ free(wm);
+
+ return;
+}
+
+
+/**
+ * @brief Initialize an array of wiimote structures.
+ *
+ * @param wiimotes Number of wiimote_t structures to create.
+ *
+ * @return An array of initialized wiimote_t structures.
+ *
+ * @see wiiuse_connect()
+ *
+ * The array returned by this function can be passed to various
+ * functions, including wiiuse_connect().
+ */
+struct wiimote_t** wiiuse_init(int wiimotes) {
+ int i = 0;
+ struct wiimote_t** wm = NULL;
+
+ /*
+ * Please do not remove this banner.
+ * GPL asks that you please leave output credits intact.
+ * Thank you.
+ *
+ * This banner is only displayed once so that if you need
+ * to call this function again it won't be intrusive.
+ */
+ if (!g_banner) {
+ printf( "wiiuse v" WIIUSE_VERSION " loaded.\n"
+ " By: Michael Laforest <thepara[at]gmail{dot}com>\n"
+ " http://wiiuse.net http://wiiuse.sf.net\n");
+ g_banner = 1;
+ }
+
+ if (!wiimotes)
+ return NULL;
+
+ wm = malloc(sizeof(struct wiimote_t*) * wiimotes);
+
+ for (i = 0; i < wiimotes; ++i) {
+ wm[i] = malloc(sizeof(struct wiimote_t));
+ memset(wm[i], 0, sizeof(struct wiimote_t));
+
+ wm[i]->unid = i+1;
+
+ #ifndef WIN32
+ wm[i]->bdaddr = *BDADDR_ANY;
+ wm[i]->out_sock = -1;
+ wm[i]->in_sock = -1;
+ #else
+ wm[i]->dev_handle = 0;
+ wm[i]->stack = WIIUSE_STACK_UNKNOWN;
+ wm[i]->normal_timeout = WIIMOTE_DEFAULT_TIMEOUT;
+ wm[i]->exp_timeout = WIIMOTE_EXP_TIMEOUT;
+ wm[i]->timeout = wm[i]->normal_timeout;
+ #endif
+
+ wm[i]->state = WIIMOTE_INIT_STATES;
+ wm[i]->flags = WIIUSE_INIT_FLAGS;
+
+ wm[i]->event = WIIUSE_NONE;
+
+ wm[i]->exp.type = EXP_NONE;
+
+ wiiuse_set_aspect_ratio(wm[i], WIIUSE_ASPECT_4_3);
+ wiiuse_set_ir_position(wm[i], WIIUSE_IR_ABOVE);
+
+ wm[i]->orient_threshold = 0.5f;
+ wm[i]->accel_threshold = 5;
+
+ wm[i]->accel_calib.st_alpha = WIIUSE_DEFAULT_SMOOTH_ALPHA;
+ }
+
+ return wm;
+}
+
+
+/**
+ * @brief The wiimote disconnected.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ */
+void wiiuse_disconnected(struct wiimote_t* wm) {
+ if (!wm) return;
+
+ WIIUSE_INFO("Wiimote disconnected [id %i].", wm->unid);
+
+ /* disable the connected flag */
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_CONNECTED);
+
+ /* reset a bunch of stuff */
+ #ifndef WIN32
+ wm->out_sock = -1;
+ wm->in_sock = -1;
+ #else
+ wm->dev_handle = 0;
+ #endif
+
+ wm->leds = 0;
+ wm->state = WIIMOTE_INIT_STATES;
+ wm->read_req = NULL;
+ wm->handshake_state = 0;
+ wm->btns = 0;
+ wm->btns_held = 0;
+ wm->btns_released = 0;
+ memset(wm->event_buf, 0, sizeof(wm->event_buf));
+
+ wm->event = WIIUSE_DISCONNECT;
+}
+
+
+/**
+ * @brief Enable or disable the rumble.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param status 1 to enable, 0 to disable.
+ */
+void wiiuse_rumble(struct wiimote_t* wm, int status) {
+ byte buf;
+
+ if (!wm || !WIIMOTE_IS_CONNECTED(wm))
+ return;
+
+ /* make sure to keep the current lit leds */
+ buf = wm->leds;
+
+ if (status) {
+ WIIUSE_DEBUG("Starting rumble...");
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_RUMBLE);
+ buf |= 0x01;
+ } else {
+ WIIUSE_DEBUG("Stopping rumble...");
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_RUMBLE);
+ }
+
+ /* preserve IR state */
+ if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR))
+ buf |= 0x04;
+
+ wiiuse_send(wm, WM_CMD_RUMBLE, &buf, 1);
+}
+
+
+/**
+ * @brief Toggle the state of the rumble.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ */
+void wiiuse_toggle_rumble(struct wiimote_t* wm) {
+ if (!wm) return;
+
+ wiiuse_rumble(wm, !WIIMOTE_IS_SET(wm, WIIMOTE_STATE_RUMBLE));
+}
+
+
+/**
+ * @brief Set the enabled LEDs.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param leds What LEDs to enable.
+ *
+ * \a leds is a bitwise or of WIIMOTE_LED_1, WIIMOTE_LED_2, WIIMOTE_LED_3, or WIIMOTE_LED_4.
+ */
+void wiiuse_set_leds(struct wiimote_t* wm, int leds) {
+ byte buf;
+
+ if (!wm || !WIIMOTE_IS_CONNECTED(wm))
+ return;
+
+ /* remove the lower 4 bits because they control rumble */
+ wm->leds = (leds & 0xF0);
+
+ /* make sure if the rumble is on that we keep it on */
+ if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_RUMBLE))
+ wm->leds |= 0x01;
+
+ buf = wm->leds;
+
+ wiiuse_send(wm, WM_CMD_LED, &buf, 1);
+}
+
+
+/**
+ * @brief Set if the wiimote should report motion sensing.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param status 1 to enable, 0 to disable.
+ *
+ * Since reporting motion sensing sends a lot of data,
+ * the wiimote saves power by not transmitting it
+ * by default.
+ */
+void wiiuse_motion_sensing(struct wiimote_t* wm, int status) {
+ if (status)
+ WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_ACC);
+ else
+ WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_ACC);
+
+ wiiuse_set_report_type(wm);
+}
+
+
+/**
+ * @brief Set the report type based on the current wiimote state.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ *
+ * @return The report type sent.
+ *
+ * The wiimote reports formatted packets depending on the
+ * report type that was last requested. This function will
+ * update the type of report that should be sent based on
+ * the current state of the device.
+ */
+int wiiuse_set_report_type(struct wiimote_t* wm) {
+ byte buf[2];
+ int motion, exp, ir;
+
+ if (!wm || !WIIMOTE_IS_CONNECTED(wm))
+ return 0;
+
+ buf[0] = (WIIMOTE_IS_FLAG_SET(wm, WIIUSE_CONTINUOUS) ? 0x04 : 0x00); /* set to 0x04 for continuous reporting */
+ buf[1] = 0x00;
+
+ /* if rumble is enabled, make sure we keep it */
+ if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_RUMBLE))
+ buf[0] |= 0x01;
+
+ motion = WIIMOTE_IS_SET(wm, WIIMOTE_STATE_ACC);
+ exp = WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP);
+ ir = WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR);
+
+ if (motion && ir && exp) buf[1] = WM_RPT_BTN_ACC_IR_EXP;
+ else if (motion && exp) buf[1] = WM_RPT_BTN_ACC_EXP;
+ else if (motion && ir) buf[1] = WM_RPT_BTN_ACC_IR;
+ else if (ir && exp) buf[1] = WM_RPT_BTN_IR_EXP;
+ else if (ir) buf[1] = WM_RPT_BTN_ACC_IR;
+ else if (exp) buf[1] = WM_RPT_BTN_EXP;
+ else if (motion) buf[1] = WM_RPT_BTN_ACC;
+ else buf[1] = WM_RPT_BTN;
+
+ WIIUSE_DEBUG("Setting report type: 0x%x", buf[1]);
+
+ exp = wiiuse_send(wm, WM_CMD_REPORT_TYPE, buf, 2);
+ if (exp <= 0)
+ return exp;
+
+ return buf[1];
+}
+
+
+/**
+ * @brief Read data from the wiimote (callback version).
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param read_cb Function pointer to call when the data arrives from the wiimote.
+ * @param buffer An allocated buffer to store the data as it arrives from the wiimote.
+ * Must be persistent in memory and large enough to hold the data.
+ * @param addr The address of wiimote memory to read from.
+ * @param len The length of the block to be read.
+ *
+ * The library can only handle one data read request at a time
+ * because it must keep track of the buffer and other
+ * events that are specific to that request. So if a request
+ * has already been made, subsequent requests will be added
+ * to a pending list and be sent out when the previous
+ * finishes.
+ */
+int wiiuse_read_data_cb(struct wiimote_t* wm, wiiuse_read_cb read_cb, byte* buffer, unsigned int addr, unsigned short len) {
+ struct read_req_t* req;
+
+ if (!wm || !WIIMOTE_IS_CONNECTED(wm))
+ return 0;
+ if (!buffer || !len || !read_cb)
+ return 0;
+
+ /* make this request structure */
+ req = (struct read_req_t*)malloc(sizeof(struct read_req_t));
+ req->cb = read_cb;
+ req->buf = buffer;
+ req->addr = addr;
+ req->size = len;
+ req->wait = len;
+ req->dirty = 0;
+ req->next = NULL;
+
+ /* add this to the request list */
+ if (!wm->read_req) {
+ /* root node */
+ wm->read_req = req;
+
+ WIIUSE_DEBUG("Data read request can be sent out immediately.");
+
+ /* send the request out immediately */
+ wiiuse_send_next_pending_read_request(wm);
+ } else {
+ struct read_req_t* nptr = wm->read_req;
+ for (; nptr->next; nptr = nptr->next);
+ nptr->next = req;
+
+ WIIUSE_DEBUG("Added pending data read request.");
+ }
+
+ return 1;
+}
+
+
+/**
+ * @brief Read data from the wiimote (event version).
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param buffer An allocated buffer to store the data as it arrives from the wiimote.
+ * Must be persistent in memory and large enough to hold the data.
+ * @param addr The address of wiimote memory to read from.
+ * @param len The length of the block to be read.
+ *
+ * The library can only handle one data read request at a time
+ * because it must keep track of the buffer and other
+ * events that are specific to that request. So if a request
+ * has already been made, subsequent requests will be added
+ * to a pending list and be sent out when the previous
+ * finishes.
+ */
+int wiiuse_read_data(struct wiimote_t* wm, byte* buffer, unsigned int addr, unsigned short len) {
+ struct read_req_t* req;
+
+ if (!wm || !WIIMOTE_IS_CONNECTED(wm))
+ return 0;
+ if (!buffer || !len)
+ return 0;
+
+ /* make this request structure */
+ req = (struct read_req_t*)malloc(sizeof(struct read_req_t));
+ req->cb = NULL;
+ req->buf = buffer;
+ req->addr = addr;
+ req->size = len;
+ req->wait = len;
+ req->dirty = 0;
+ req->next = NULL;
+
+ /* add this to the request list */
+ if (!wm->read_req) {
+ /* root node */
+ wm->read_req = req;
+
+ WIIUSE_DEBUG("Data read request can be sent out immediately.");
+
+ /* send the request out immediately */
+ wiiuse_send_next_pending_read_request(wm);
+ } else {
+ struct read_req_t* nptr = wm->read_req;
+ for (; nptr->next; nptr = nptr->next);
+ nptr->next = req;
+
+ WIIUSE_DEBUG("Added pending data read request.");
+ }
+
+ return 1;
+}
+
+
+/**
+ * @brief Send the next pending data read request to the wiimote.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ *
+ * @see wiiuse_read_data()
+ *
+ * This function is not part of the wiiuse API.
+ */
+void wiiuse_send_next_pending_read_request(struct wiimote_t* wm) {
+ byte buf[6];
+ struct read_req_t* req;
+
+ if (!wm || !WIIMOTE_IS_CONNECTED(wm))
+ return;
+ if (!wm->read_req) return;
+
+ /* skip over dirty ones since they have already been read */
+ req = wm->read_req;
+ while (req && req->dirty)
+ req = req->next;
+ if (!req)
+ return;
+
+ /* the offset is in big endian */
+ *(int*)(buf) = BIG_ENDIAN_LONG(req->addr);
+
+ /* the length is in big endian */
+ *(short*)(buf + 4) = BIG_ENDIAN_SHORT(req->size);
+
+ WIIUSE_DEBUG("Request read at address: 0x%x length: %i", req->addr, req->size);
+ wiiuse_send(wm, WM_CMD_READ_DATA, buf, 6);
+}
+
+
+/**
+ * @brief Request the wiimote controller status.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ *
+ * Controller status includes: battery level, LED status, expansions
+ */
+void wiiuse_status(struct wiimote_t* wm) {
+ byte buf = 0;
+
+ if (!wm || !WIIMOTE_IS_CONNECTED(wm))
+ return;
+
+ WIIUSE_DEBUG("Requested wiimote status.");
+
+ wiiuse_send(wm, WM_CMD_CTRL_STATUS, &buf, 1);
+}
+
+
+/**
+ * @brief Find a wiimote_t structure by its unique identifier.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param wiimotes The number of wiimote_t structures in \a wm.
+ * @param unid The unique identifier to search for.
+ *
+ * @return Pointer to a wiimote_t structure, or NULL if not found.
+ */
+struct wiimote_t* wiiuse_get_by_id(struct wiimote_t** wm, int wiimotes, int unid) {
+ int i = 0;
+
+ for (; i < wiimotes; ++i) {
+ if (wm[i]->unid == unid)
+ return wm[i];
+ }
+
+ return NULL;
+}
+
+
+/**
+ * @brief Write data to the wiimote.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param addr The address to write to.
+ * @param data The data to be written to the memory location.
+ * @param len The length of the block to be written.
+ */
+int wiiuse_write_data(struct wiimote_t* wm, unsigned int addr, byte* data, byte len) {
+ byte buf[21] = {0}; /* the payload is always 23 */
+
+ if (!wm || !WIIMOTE_IS_CONNECTED(wm))
+ return 0;
+ if (!data || !len)
+ return 0;
+
+ WIIUSE_DEBUG("Writing %i bytes to memory location 0x%x...", len, addr);
+
+ #ifdef WITH_WIIUSE_DEBUG
+ {
+ int i = 0;
+ printf("Write data is: ");
+ for (; i < len; ++i)
+ printf("%x ", data[i]);
+ printf("\n");
+ }
+ #endif
+
+ /* the offset is in big endian */
+ *(int*)(buf) = BIG_ENDIAN_LONG(addr);
+
+ /* length */
+ *(byte*)(buf + 4) = len;
+
+ /* data */
+ memcpy(buf + 5, data, len);
+
+ wiiuse_send(wm, WM_CMD_WRITE_DATA, buf, 21);
+ return 1;
+}
+
+
+/**
+ * @brief Send a packet to the wiimote.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param report_type The report type to send (WIIMOTE_CMD_LED, WIIMOTE_CMD_RUMBLE, etc). Found in wiiuse.h
+ * @param msg The payload.
+ * @param len Length of the payload in bytes.
+ *
+ * This function should replace any write()s directly to the wiimote device.
+ */
+int wiiuse_send(struct wiimote_t* wm, byte report_type, byte* msg, int len) {
+ byte buf[32]; /* no payload is better than this */
+ int rumble = 0;
+
+ #ifndef WIN32
+ buf[0] = WM_SET_REPORT | WM_BT_OUTPUT;
+ buf[1] = report_type;
+ #else
+ buf[0] = report_type;
+ #endif
+
+ switch (report_type) {
+ case WM_CMD_LED:
+ case WM_CMD_RUMBLE:
+ case WM_CMD_CTRL_STATUS:
+ {
+ /* Rumble flag for: 0x11, 0x13, 0x14, 0x15, 0x19 or 0x1a */
+ if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_RUMBLE))
+ rumble = 1;
+ break;
+ }
+ default:
+ break;
+ }
+
+ #ifndef WIN32
+ memcpy(buf+2, msg, len);
+ if (rumble)
+ buf[2] |= 0x01;
+ #else
+ memcpy(buf+1, msg, len);
+ if (rumble)
+ buf[1] |= 0x01;
+ #endif
+
+ #ifdef WITH_WIIUSE_DEBUG
+ {
+ int x = 2;
+ printf("[DEBUG] (id %i) SEND: (%x) %.2x ", wm->unid, buf[0], buf[1]);
+ #ifndef WIN32
+ for (; x < len+2; ++x)
+ #else
+ for (; x < len+1; ++x)
+ #endif
+ printf("%.2x ", buf[x]);
+ printf("\n");
+ }
+ #endif
+
+ #ifndef WIN32
+ return wiiuse_io_write(wm, buf, len+2);
+ #else
+ return wiiuse_io_write(wm, buf, len+1);
+ #endif
+}
+
+
+/**
+ * @brief Set flags for the specified wiimote.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param enable Flags to enable.
+ * @param disable Flags to disable.
+ *
+ * @return The flags set after 'enable' and 'disable' have been applied.
+ *
+ * The values 'enable' and 'disable' may be any flags OR'ed together.
+ * Flags are defined in wiiuse.h.
+ */
+int wiiuse_set_flags(struct wiimote_t* wm, int enable, int disable) {
+ if (!wm) return 0;
+
+ /* remove mutually exclusive flags */
+ enable &= ~disable;
+ disable &= ~enable;
+
+ wm->flags |= enable;
+ wm->flags &= ~disable;
+
+ return wm->flags;
+}
+
+
+/**
+ * @brief Set the wiimote smoothing alpha value.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param alpha The alpha value to set. Between 0 and 1.
+ *
+ * @return Returns the old alpha value.
+ *
+ * The alpha value is between 0 and 1 and is used in an exponential
+ * smoothing algorithm.
+ *
+ * Smoothing is only performed if the WIIMOTE_USE_SMOOTHING is set.
+ */
+float wiiuse_set_smooth_alpha(struct wiimote_t* wm, float alpha) {
+ float old;
+
+ if (!wm) return 0.0f;
+
+ old = wm->accel_calib.st_alpha;
+
+ wm->accel_calib.st_alpha = alpha;
+
+ /* if there is a nunchuk set that too */
+ if (wm->exp.type == EXP_NUNCHUK)
+ wm->exp.nunchuk.accel_calib.st_alpha = alpha;
+
+ return old;
+}
+
+
+/**
+ * @brief Set the bluetooth stack type to use.
+ *
+ * @param wm Array of wiimote_t structures.
+ * @param wiimotes Number of objects in the wm array.
+ * @param type The type of bluetooth stack to use.
+ */
+void wiiuse_set_bluetooth_stack(struct wiimote_t** wm, int wiimotes, enum win_bt_stack_t type) {
+ #ifdef WIN32
+ int i;
+
+ if (!wm) return;
+
+ for (i = 0; i < wiimotes; ++i)
+ wm[i]->stack = type;
+ #endif
+}
+
+
+/**
+ * @brief Set the orientation event threshold.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param threshold The decimal place that should be considered a significant change.
+ *
+ * If threshold is 0.01, and any angle changes by 0.01 then a significant change
+ * has occured and the event callback will be invoked. If threshold is 1 then
+ * the angle has to change by a full degree to generate an event.
+ */
+void wiiuse_set_orient_threshold(struct wiimote_t* wm, float threshold) {
+ if (!wm) return;
+
+ wm->orient_threshold = threshold;
+}
+
+
+/**
+ * @brief Set the accelerometer event threshold.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param threshold The decimal place that should be considered a significant change.
+ */
+void wiiuse_set_accel_threshold(struct wiimote_t* wm, int threshold) {
+ if (!wm) return;
+
+ wm->accel_threshold = threshold;
+}
+
+
+/**
+ * @brief Try to resync with the wiimote by starting a new handshake.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ */
+void wiiuse_resync(struct wiimote_t* wm) {
+ if (!wm) return;
+
+ wm->handshake_state = 0;
+ wiiuse_handshake(wm, NULL, 0);
+}
+
+
+/**
+ * @brief Set the normal and expansion handshake timeouts.
+ *
+ * @param wm Array of wiimote_t structures.
+ * @param wiimotes Number of objects in the wm array.
+ * @param normal_timeout The timeout in milliseconds for a normal read.
+ * @param exp_timeout The timeout in millisecondsd to wait for an expansion handshake.
+ */
+void wiiuse_set_timeout(struct wiimote_t** wm, int wiimotes, byte normal_timeout, byte exp_timeout) {
+ #ifdef WIN32
+ int i;
+
+ if (!wm) return;
+
+ for (i = 0; i < wiimotes; ++i) {
+ wm[i]->normal_timeout = normal_timeout;
+ wm[i]->exp_timeout = exp_timeout;
+ }
+ #endif
+}
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse.h b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse.h
new file mode 100644
index 0000000000..9dff81c8e4
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse.h
@@ -0,0 +1,653 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ *
+ * @brief API header file.
+ *
+ * If this file is included from inside the wiiuse source
+ * and not from a third party program, then wiimote_internal.h
+ * is also included which extends this file.
+ */
+
+#ifndef WIIUSE_H_INCLUDED
+#define WIIUSE_H_INCLUDED
+
+#ifdef _WIN32
+ /* windows */
+ #include <windows.h>
+#else
+ /* nix */
+ #include <bluetooth/bluetooth.h>
+#endif
+
+#ifdef WIIUSE_INTERNAL_H_INCLUDED
+ #define WCONST
+#else
+ #define WCONST const
+#endif
+
+/* led bit masks */
+#define WIIMOTE_LED_NONE 0x00
+#define WIIMOTE_LED_1 0x10
+#define WIIMOTE_LED_2 0x20
+#define WIIMOTE_LED_3 0x40
+#define WIIMOTE_LED_4 0x80
+
+/* button codes */
+#define WIIMOTE_BUTTON_TWO 0x0001
+#define WIIMOTE_BUTTON_ONE 0x0002
+#define WIIMOTE_BUTTON_B 0x0004
+#define WIIMOTE_BUTTON_A 0x0008
+#define WIIMOTE_BUTTON_MINUS 0x0010
+#define WIIMOTE_BUTTON_ZACCEL_BIT6 0x0020
+#define WIIMOTE_BUTTON_ZACCEL_BIT7 0x0040
+#define WIIMOTE_BUTTON_HOME 0x0080
+#define WIIMOTE_BUTTON_LEFT 0x0100
+#define WIIMOTE_BUTTON_RIGHT 0x0200
+#define WIIMOTE_BUTTON_DOWN 0x0400
+#define WIIMOTE_BUTTON_UP 0x0800
+#define WIIMOTE_BUTTON_PLUS 0x1000
+#define WIIMOTE_BUTTON_ZACCEL_BIT4 0x2000
+#define WIIMOTE_BUTTON_ZACCEL_BIT5 0x4000
+#define WIIMOTE_BUTTON_UNKNOWN 0x8000
+#define WIIMOTE_BUTTON_ALL 0x1F9F
+
+/* nunchul button codes */
+#define NUNCHUK_BUTTON_Z 0x01
+#define NUNCHUK_BUTTON_C 0x02
+#define NUNCHUK_BUTTON_ALL 0x03
+
+/* classic controller button codes */
+#define CLASSIC_CTRL_BUTTON_UP 0x0001
+#define CLASSIC_CTRL_BUTTON_LEFT 0x0002
+#define CLASSIC_CTRL_BUTTON_ZR 0x0004
+#define CLASSIC_CTRL_BUTTON_X 0x0008
+#define CLASSIC_CTRL_BUTTON_A 0x0010
+#define CLASSIC_CTRL_BUTTON_Y 0x0020
+#define CLASSIC_CTRL_BUTTON_B 0x0040
+#define CLASSIC_CTRL_BUTTON_ZL 0x0080
+#define CLASSIC_CTRL_BUTTON_FULL_R 0x0200
+#define CLASSIC_CTRL_BUTTON_PLUS 0x0400
+#define CLASSIC_CTRL_BUTTON_HOME 0x0800
+#define CLASSIC_CTRL_BUTTON_MINUS 0x1000
+#define CLASSIC_CTRL_BUTTON_FULL_L 0x2000
+#define CLASSIC_CTRL_BUTTON_DOWN 0x4000
+#define CLASSIC_CTRL_BUTTON_RIGHT 0x8000
+#define CLASSIC_CTRL_BUTTON_ALL 0xFEFF
+
+/* guitar hero 3 button codes */
+#define GUITAR_HERO_3_BUTTON_STRUM_UP 0x0001
+#define GUITAR_HERO_3_BUTTON_YELLOW 0x0008
+#define GUITAR_HERO_3_BUTTON_GREEN 0x0010
+#define GUITAR_HERO_3_BUTTON_BLUE 0x0020
+#define GUITAR_HERO_3_BUTTON_RED 0x0040
+#define GUITAR_HERO_3_BUTTON_ORANGE 0x0080
+#define GUITAR_HERO_3_BUTTON_PLUS 0x0400
+#define GUITAR_HERO_3_BUTTON_MINUS 0x1000
+#define GUITAR_HERO_3_BUTTON_STRUM_DOWN 0x4000
+#define GUITAR_HERO_3_BUTTON_ALL 0xFEFF
+
+
+/* wiimote option flags */
+#define WIIUSE_SMOOTHING 0x01
+#define WIIUSE_CONTINUOUS 0x02
+#define WIIUSE_ORIENT_THRESH 0x04
+#define WIIUSE_INIT_FLAGS (WIIUSE_SMOOTHING | WIIUSE_ORIENT_THRESH)
+
+#define WIIUSE_ORIENT_PRECISION 100.0f
+
+/* expansion codes */
+#define EXP_NONE 0
+#define EXP_NUNCHUK 1
+#define EXP_CLASSIC 2
+#define EXP_GUITAR_HERO_3 3
+
+/* IR correction types */
+typedef enum ir_position_t {
+ WIIUSE_IR_ABOVE,
+ WIIUSE_IR_BELOW
+} ir_position_t;
+
+/**
+ * @brief Check if a button is pressed.
+ * @param dev Pointer to a wiimote_t or expansion structure.
+ * @param button The button you are interested in.
+ * @return 1 if the button is pressed, 0 if not.
+ */
+#define IS_PRESSED(dev, button) ((dev->btns & button) == button)
+
+/**
+ * @brief Check if a button is being held.
+ * @param dev Pointer to a wiimote_t or expansion structure.
+ * @param button The button you are interested in.
+ * @return 1 if the button is held, 0 if not.
+ */
+#define IS_HELD(dev, button) ((dev->btns_held & button) == button)
+
+/**
+ * @brief Check if a button is released on this event. \n\n
+ * This does not mean the button is not pressed, it means \n
+ * this button was just now released.
+ * @param dev Pointer to a wiimote_t or expansion structure.
+ * @param button The button you are interested in.
+ * @return 1 if the button is released, 0 if not.
+ *
+ */
+#define IS_RELEASED(dev, button) ((dev->btns_released & button) == button)
+
+/**
+ * @brief Check if a button has just been pressed this event.
+ * @param dev Pointer to a wiimote_t or expansion structure.
+ * @param button The button you are interested in.
+ * @return 1 if the button is pressed, 0 if not.
+ */
+#define IS_JUST_PRESSED(dev, button) (IS_PRESSED(dev, button) && !IS_HELD(dev, button))
+
+/**
+ * @brief Return the IR sensitivity level.
+ * @param wm Pointer to a wiimote_t structure.
+ * @param lvl [out] Pointer to an int that will hold the level setting.
+ * If no level is set 'lvl' will be set to 0.
+ */
+#define WIIUSE_GET_IR_SENSITIVITY(dev, lvl) \
+ do { \
+ if ((wm->state & 0x0200) == 0x0200) *lvl = 1; \
+ else if ((wm->state & 0x0400) == 0x0400) *lvl = 2; \
+ else if ((wm->state & 0x0800) == 0x0800) *lvl = 3; \
+ else if ((wm->state & 0x1000) == 0x1000) *lvl = 4; \
+ else if ((wm->state & 0x2000) == 0x2000) *lvl = 5; \
+ else *lvl = 0; \
+ } while (0)
+
+#define WIIUSE_USING_ACC(wm) ((wm->state & 0x020) == 0x020)
+#define WIIUSE_USING_EXP(wm) ((wm->state & 0x040) == 0x040)
+#define WIIUSE_USING_IR(wm) ((wm->state & 0x080) == 0x080)
+#define WIIUSE_USING_SPEAKER(wm) ((wm->state & 0x100) == 0x100)
+
+#define WIIUSE_IS_LED_SET(wm, num) ((wm->leds & WIIMOTE_LED_##num) == WIIMOTE_LED_##num)
+
+/*
+ * Largest known payload is 21 bytes.
+ * Add 2 for the prefix and round up to a power of 2.
+ */
+#define MAX_PAYLOAD 32
+
+/*
+ * This is left over from an old hack, but it may actually
+ * be a useful feature to keep so it wasn't removed.
+ */
+#ifdef WIN32
+ #define WIIMOTE_DEFAULT_TIMEOUT 10
+ #define WIIMOTE_EXP_TIMEOUT 10
+#endif
+
+typedef unsigned char byte;
+typedef char sbyte;
+
+struct wiimote_t;
+struct vec3b_t;
+struct orient_t;
+struct gforce_t;
+
+
+/**
+ * @brief Callback that handles a read event.
+ *
+ * @param wm Pointer to a wiimote_t structure.
+ * @param data Pointer to the filled data block.
+ * @param len Length in bytes of the data block.
+ *
+ * @see wiiuse_init()
+ *
+ * A registered function of this type is called automatically by the wiiuse
+ * library when the wiimote has returned the full data requested by a previous
+ * call to wiiuse_read_data().
+ */
+typedef void (*wiiuse_read_cb)(struct wiimote_t* wm, byte* data, unsigned short len);
+
+
+/**
+ * @struct read_req_t
+ * @brief Data read request structure.
+ */
+struct read_req_t {
+ wiiuse_read_cb cb; /**< read data callback */
+ byte* buf; /**< buffer where read data is written */
+ unsigned int addr; /**< the offset that the read started at */
+ unsigned short size; /**< the length of the data read */
+ unsigned short wait; /**< num bytes still needed to finish read */
+ byte dirty; /**< set to 1 if not using callback and needs to be cleaned up */
+
+ struct read_req_t* next; /**< next read request in the queue */
+};
+
+
+/**
+ * @struct vec2b_t
+ * @brief Unsigned x,y byte vector.
+ */
+typedef struct vec2b_t {
+ byte x, y;
+} vec2b_t;
+
+
+/**
+ * @struct vec3b_t
+ * @brief Unsigned x,y,z byte vector.
+ */
+typedef struct vec3b_t {
+ byte x, y, z;
+} vec3b_t;
+
+
+/**
+ * @struct vec3f_t
+ * @brief Signed x,y,z float struct.
+ */
+typedef struct vec3f_t {
+ float x, y, z;
+} vec3f_t;
+
+
+/**
+ * @struct orient_t
+ * @brief Orientation struct.
+ *
+ * Yaw, pitch, and roll range from -180 to 180 degrees.
+ */
+typedef struct orient_t {
+ float roll; /**< roll, this may be smoothed if enabled */
+ float pitch; /**< pitch, this may be smoothed if enabled */
+ float yaw;
+
+ float a_roll; /**< absolute roll, unsmoothed */
+ float a_pitch; /**< absolute pitch, unsmoothed */
+} orient_t;
+
+
+/**
+ * @struct gforce_t
+ * @brief Gravity force struct.
+ */
+typedef struct gforce_t {
+ float x, y, z;
+} gforce_t;
+
+
+/**
+ * @struct accel_t
+ * @brief Accelerometer struct. For any device with an accelerometer.
+ */
+typedef struct accel_t {
+ struct vec3b_t cal_zero; /**< zero calibration */
+ struct vec3b_t cal_g; /**< 1g difference around 0cal */
+
+ float st_roll; /**< last smoothed roll value */
+ float st_pitch; /**< last smoothed roll pitch */
+ float st_alpha; /**< alpha value for smoothing [0-1] */
+} accel_t;
+
+
+/**
+ * @struct ir_dot_t
+ * @brief A single IR source.
+ */
+typedef struct ir_dot_t {
+ byte visible; /**< if the IR source is visible */
+
+ unsigned int x; /**< interpolated X coordinate */
+ unsigned int y; /**< interpolated Y coordinate */
+
+ short rx; /**< raw X coordinate (0-1023) */
+ short ry; /**< raw Y coordinate (0-767) */
+
+ byte order; /**< increasing order by x-axis value */
+
+ byte size; /**< size of the IR dot (0-15) */
+} ir_dot_t;
+
+
+/**
+ * @enum aspect_t
+ * @brief Screen aspect ratio.
+ */
+typedef enum aspect_t {
+ WIIUSE_ASPECT_4_3,
+ WIIUSE_ASPECT_16_9
+} aspect_t;
+
+
+/**
+ * @struct ir_t
+ * @brief IR struct. Hold all data related to the IR tracking.
+ */
+typedef struct ir_t {
+ struct ir_dot_t dot[4]; /**< IR dots */
+ byte num_dots; /**< number of dots at this time */
+
+ enum aspect_t aspect; /**< aspect ratio of the screen */
+
+ enum ir_position_t pos; /**< IR sensor bar position */
+
+ unsigned int vres[2]; /**< IR virtual screen resolution */
+ int offset[2]; /**< IR XY correction offset */
+ int state; /**< keeps track of the IR state */
+
+ int ax; /**< absolute X coordinate */
+ int ay; /**< absolute Y coordinate */
+
+ int x; /**< calculated X coordinate */
+ int y; /**< calculated Y coordinate */
+
+ float distance; /**< pixel distance between first 2 dots*/
+ float z; /**< calculated distance */
+} ir_t;
+
+
+/**
+ * @struct joystick_t
+ * @brief Joystick calibration structure.
+ *
+ * The angle \a ang is relative to the positive y-axis into quadrant I
+ * and ranges from 0 to 360 degrees. So if the joystick is held straight
+ * upwards then angle is 0 degrees. If it is held to the right it is 90,
+ * down is 180, and left is 270.
+ *
+ * The magnitude \a mag is the distance from the center to where the
+ * joystick is being held. The magnitude ranges from 0 to 1.
+ * If the joystick is only slightly tilted from the center the magnitude
+ * will be low, but if it is closer to the outter edge the value will
+ * be higher.
+ */
+typedef struct joystick_t {
+ struct vec2b_t max; /**< maximum joystick values */
+ struct vec2b_t min; /**< minimum joystick values */
+ struct vec2b_t center; /**< center joystick values */
+
+ float ang; /**< angle the joystick is being held */
+ float mag; /**< magnitude of the joystick (range 0-1) */
+} joystick_t;
+
+
+/**
+ * @struct nunchuk_t
+ * @brief Nunchuk expansion device.
+ */
+typedef struct nunchuk_t {
+ struct accel_t accel_calib; /**< nunchuk accelerometer calibration */
+ struct joystick_t js; /**< joystick calibration */
+
+ int* flags; /**< options flag (points to wiimote_t.flags) */
+
+ byte btns; /**< what buttons have just been pressed */
+ byte btns_held; /**< what buttons are being held down */
+ byte btns_released; /**< what buttons were just released this */
+
+ float orient_threshold; /**< threshold for orient to generate an event */
+ int accel_threshold; /**< threshold for accel to generate an event */
+
+ struct vec3b_t accel; /**< current raw acceleration data */
+ struct orient_t orient; /**< current orientation on each axis */
+ struct gforce_t gforce; /**< current gravity forces on each axis */
+} nunchuk_t;
+
+
+/**
+ * @struct classic_ctrl_t
+ * @brief Classic controller expansion device.
+ */
+typedef struct classic_ctrl_t {
+ short btns; /**< what buttons have just been pressed */
+ short btns_held; /**< what buttons are being held down */
+ short btns_released; /**< what buttons were just released this */
+
+ float r_shoulder; /**< right shoulder button (range 0-1) */
+ float l_shoulder; /**< left shoulder button (range 0-1) */
+
+ struct joystick_t ljs; /**< left joystick calibration */
+ struct joystick_t rjs; /**< right joystick calibration */
+} classic_ctrl_t;
+
+
+/**
+ * @struct guitar_hero_3_t
+ * @brief Guitar Hero 3 expansion device.
+ */
+typedef struct guitar_hero_3_t {
+ short btns; /**< what buttons have just been pressed */
+ short btns_held; /**< what buttons are being held down */
+ short btns_released; /**< what buttons were just released this */
+
+ float whammy_bar; /**< whammy bar (range 0-1) */
+
+ struct joystick_t js; /**< joystick calibration */
+} guitar_hero_3_t;
+
+
+/**
+ * @struct expansion_t
+ * @brief Generic expansion device plugged into wiimote.
+ */
+typedef struct expansion_t {
+ int type; /**< type of expansion attached */
+
+ union {
+ struct nunchuk_t nunchuk;
+ struct classic_ctrl_t classic;
+ struct guitar_hero_3_t gh3;
+ };
+} expansion_t;
+
+
+/**
+ * @enum win32_bt_stack_t
+ * @brief Available bluetooth stacks for Windows.
+ */
+typedef enum win_bt_stack_t {
+ WIIUSE_STACK_UNKNOWN,
+ WIIUSE_STACK_MS,
+ WIIUSE_STACK_BLUESOLEIL
+} win_bt_stack_t;
+
+
+/**
+ * @struct wiimote_state_t
+ * @brief Significant data from the previous event.
+ */
+typedef struct wiimote_state_t {
+ /* expansion_t */
+ float exp_ljs_ang;
+ float exp_rjs_ang;
+ float exp_ljs_mag;
+ float exp_rjs_mag;
+ unsigned short exp_btns;
+ struct orient_t exp_orient;
+ struct vec3b_t exp_accel;
+ float exp_r_shoulder;
+ float exp_l_shoulder;
+
+ /* ir_t */
+ int ir_ax;
+ int ir_ay;
+ float ir_distance;
+
+ struct orient_t orient;
+ unsigned short btns;
+
+ struct vec3b_t accel;
+} wiimote_state_t;
+
+
+/**
+ * @enum WIIUSE_EVENT_TYPE
+ * @brief Events that wiiuse can generate from a poll.
+ */
+typedef enum WIIUSE_EVENT_TYPE {
+ WIIUSE_NONE = 0,
+ WIIUSE_EVENT,
+ WIIUSE_STATUS,
+ WIIUSE_CONNECT,
+ WIIUSE_DISCONNECT,
+ WIIUSE_UNEXPECTED_DISCONNECT,
+ WIIUSE_READ_DATA,
+ WIIUSE_NUNCHUK_INSERTED,
+ WIIUSE_NUNCHUK_REMOVED,
+ WIIUSE_CLASSIC_CTRL_INSERTED,
+ WIIUSE_CLASSIC_CTRL_REMOVED,
+ WIIUSE_GUITAR_HERO_3_CTRL_INSERTED,
+ WIIUSE_GUITAR_HERO_3_CTRL_REMOVED
+} WIIUSE_EVENT_TYPE;
+
+/**
+ * @struct wiimote_t
+ * @brief Wiimote structure.
+ */
+typedef struct wiimote_t {
+ WCONST int unid; /**< user specified id */
+
+ #ifndef WIN32
+ WCONST bdaddr_t bdaddr; /**< bt address */
+ WCONST char bdaddr_str[18]; /**< readable bt address */
+ WCONST int out_sock; /**< output socket */
+ WCONST int in_sock; /**< input socket */
+ #else
+ WCONST HANDLE dev_handle; /**< HID handle */
+ WCONST OVERLAPPED hid_overlap; /**< overlap handle */
+ WCONST enum win_bt_stack_t stack; /**< type of bluetooth stack to use */
+ WCONST int timeout; /**< read timeout */
+ WCONST byte normal_timeout; /**< normal timeout */
+ WCONST byte exp_timeout; /**< timeout for expansion handshake */
+ #endif
+
+ WCONST int state; /**< various state flags */
+ WCONST byte leds; /**< currently lit leds */
+ WCONST float battery_level; /**< battery level */
+
+ WCONST int flags; /**< options flag */
+
+ WCONST byte handshake_state; /**< the state of the connection handshake */
+
+ WCONST struct read_req_t* read_req; /**< list of data read requests */
+ WCONST struct accel_t accel_calib; /**< wiimote accelerometer calibration */
+ WCONST struct expansion_t exp; /**< wiimote expansion device */
+
+ WCONST struct vec3b_t accel; /**< current raw acceleration data */
+ WCONST struct orient_t orient; /**< current orientation on each axis */
+ WCONST struct gforce_t gforce; /**< current gravity forces on each axis */
+
+ WCONST struct ir_t ir; /**< IR data */
+
+ WCONST unsigned short btns; /**< what buttons have just been pressed */
+ WCONST unsigned short btns_held; /**< what buttons are being held down */
+ WCONST unsigned short btns_released; /**< what buttons were just released this */
+
+ WCONST float orient_threshold; /**< threshold for orient to generate an event */
+ WCONST int accel_threshold; /**< threshold for accel to generate an event */
+
+ WCONST struct wiimote_state_t lstate; /**< last saved state */
+
+ WCONST WIIUSE_EVENT_TYPE event; /**< type of event that occured */
+ WCONST byte event_buf[MAX_PAYLOAD]; /**< event buffer */
+} wiimote;
+
+
+/*****************************************
+ *
+ * Include API specific stuff
+ *
+ *****************************************/
+
+#ifdef _WIN32
+ #define WIIUSE_EXPORT_DECL __declspec(dllexport)
+ #define WIIUSE_IMPORT_DECL __declspec(dllimport)
+#else
+ #define WIIUSE_EXPORT_DECL
+ #define WIIUSE_IMPORT_DECL
+#endif
+
+#ifdef WIIUSE_COMPILE_LIB
+ #define WIIUSE_EXPORT WIIUSE_EXPORT_DECL
+#else
+ #define WIIUSE_EXPORT WIIUSE_IMPORT_DECL
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* wiiuse.c */
+WIIUSE_EXPORT extern const char* wiiuse_version();
+
+WIIUSE_EXPORT extern struct wiimote_t** wiiuse_init(int wiimotes);
+WIIUSE_EXPORT extern void wiiuse_disconnected(struct wiimote_t* wm);
+WIIUSE_EXPORT extern void wiiuse_cleanup(struct wiimote_t** wm, int wiimotes);
+WIIUSE_EXPORT extern void wiiuse_rumble(struct wiimote_t* wm, int status);
+WIIUSE_EXPORT extern void wiiuse_toggle_rumble(struct wiimote_t* wm);
+WIIUSE_EXPORT extern void wiiuse_set_leds(struct wiimote_t* wm, int leds);
+WIIUSE_EXPORT extern void wiiuse_motion_sensing(struct wiimote_t* wm, int status);
+WIIUSE_EXPORT extern int wiiuse_read_data(struct wiimote_t* wm, byte* buffer, unsigned int offset, unsigned short len);
+WIIUSE_EXPORT extern int wiiuse_write_data(struct wiimote_t* wm, unsigned int addr, byte* data, byte len);
+WIIUSE_EXPORT extern void wiiuse_status(struct wiimote_t* wm);
+WIIUSE_EXPORT extern struct wiimote_t* wiiuse_get_by_id(struct wiimote_t** wm, int wiimotes, int unid);
+WIIUSE_EXPORT extern int wiiuse_set_flags(struct wiimote_t* wm, int enable, int disable);
+WIIUSE_EXPORT extern float wiiuse_set_smooth_alpha(struct wiimote_t* wm, float alpha);
+WIIUSE_EXPORT extern void wiiuse_set_bluetooth_stack(struct wiimote_t** wm, int wiimotes, enum win_bt_stack_t type);
+WIIUSE_EXPORT extern void wiiuse_set_orient_threshold(struct wiimote_t* wm, float threshold);
+WIIUSE_EXPORT extern void wiiuse_resync(struct wiimote_t* wm);
+WIIUSE_EXPORT extern void wiiuse_set_timeout(struct wiimote_t** wm, int wiimotes, byte normal_timeout, byte exp_timeout);
+WIIUSE_EXPORT extern void wiiuse_set_accel_threshold(struct wiimote_t* wm, int threshold);
+
+/* connect.c */
+WIIUSE_EXPORT extern int wiiuse_find(struct wiimote_t** wm, int max_wiimotes, int timeout);
+WIIUSE_EXPORT extern int wiiuse_connect(struct wiimote_t** wm, int wiimotes);
+WIIUSE_EXPORT extern void wiiuse_disconnect(struct wiimote_t* wm);
+
+/* events.c */
+WIIUSE_EXPORT extern int wiiuse_poll(struct wiimote_t** wm, int wiimotes);
+
+/* ir.c */
+WIIUSE_EXPORT extern void wiiuse_set_ir(struct wiimote_t* wm, int status);
+WIIUSE_EXPORT extern void wiiuse_set_ir_vres(struct wiimote_t* wm, unsigned int x, unsigned int y);
+WIIUSE_EXPORT extern void wiiuse_set_ir_position(struct wiimote_t* wm, enum ir_position_t pos);
+WIIUSE_EXPORT extern void wiiuse_set_aspect_ratio(struct wiimote_t* wm, enum aspect_t aspect);
+WIIUSE_EXPORT extern void wiiuse_set_ir_sensitivity(struct wiimote_t* wm, int level);
+
+/* nunchuk.c */
+WIIUSE_EXPORT extern void wiiuse_set_nunchuk_orient_threshold(struct wiimote_t* wm, float threshold);
+WIIUSE_EXPORT extern void wiiuse_set_nunchuk_accel_threshold(struct wiimote_t* wm, int threshold);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* WIIUSE_H_INCLUDED */
+
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse_internal.h b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse_internal.h
new file mode 100644
index 0000000000..1b2f423862
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/src/wiiuse_internal.h
@@ -0,0 +1,226 @@
+/*
+ * wiiuse
+ *
+ * Written By:
+ * Michael Laforest < para >
+ * Email: < thepara (--AT--) g m a i l [--DOT--] com >
+ *
+ * Copyright 2006-2007
+ *
+ * This file is part of wiiuse.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * $Header$
+ *
+ */
+
+/**
+ * @file
+ * @brief General internal wiiuse stuff.
+ *
+ * Since Wiiuse is a library, wiiuse.h is a duplicate
+ * of the API header.
+ *
+ * The code that would normally go in that file, but
+ * which is not needed by third party developers,
+ * is put here.
+ *
+ * So wiiuse_internal.h is included by other files
+ * internally, wiiuse.h is included only here.
+ */
+
+#ifndef WIIUSE_INTERNAL_H_INCLUDED
+#define WIIUSE_INTERNAL_H_INCLUDED
+
+#ifndef WIN32
+ #include <arpa/inet.h> /* htons() */
+ #include <bluetooth/bluetooth.h>
+#endif
+
+#include "definitions.h"
+
+/* wiiuse version */
+#define WIIUSE_VERSION "0.12"
+
+/********************
+ *
+ * Wiimote internal codes
+ *
+ ********************/
+
+/* Communication channels */
+#define WM_OUTPUT_CHANNEL 0x11
+#define WM_INPUT_CHANNEL 0x13
+
+#define WM_SET_REPORT 0x50
+
+/* commands */
+#define WM_CMD_LED 0x11
+#define WM_CMD_REPORT_TYPE 0x12
+#define WM_CMD_RUMBLE 0x13
+#define WM_CMD_IR 0x13
+#define WM_CMD_CTRL_STATUS 0x15
+#define WM_CMD_WRITE_DATA 0x16
+#define WM_CMD_READ_DATA 0x17
+#define WM_CMD_IR_2 0x1A
+
+/* input report ids */
+#define WM_RPT_CTRL_STATUS 0x20
+#define WM_RPT_READ 0x21
+#define WM_RPT_WRITE 0x22
+#define WM_RPT_BTN 0x30
+#define WM_RPT_BTN_ACC 0x31
+#define WM_RPT_BTN_ACC_IR 0x33
+#define WM_RPT_BTN_EXP 0x34
+#define WM_RPT_BTN_ACC_EXP 0x35
+#define WM_RPT_BTN_IR_EXP 0x36
+#define WM_RPT_BTN_ACC_IR_EXP 0x37
+
+#define WM_BT_INPUT 0x01
+#define WM_BT_OUTPUT 0x02
+
+/* Identify the wiimote device by its class */
+#define WM_DEV_CLASS_0 0x04
+#define WM_DEV_CLASS_1 0x25
+#define WM_DEV_CLASS_2 0x00
+#define WM_VENDOR_ID 0x057E
+#define WM_PRODUCT_ID 0x0306
+
+/* controller status stuff */
+#define WM_MAX_BATTERY_CODE 0xC8
+
+/* offsets in wiimote memory */
+#define WM_MEM_OFFSET_CALIBRATION 0x16
+#define WM_EXP_MEM_BASE 0x04A40000
+#define WM_EXP_MEM_ENABLE 0x04A40040
+#define WM_EXP_MEM_CALIBR 0x04A40020
+
+#define WM_REG_IR 0x04B00030
+#define WM_REG_IR_BLOCK1 0x04B00000
+#define WM_REG_IR_BLOCK2 0x04B0001A
+#define WM_REG_IR_MODENUM 0x04B00033
+
+/* ir block data */
+#define WM_IR_BLOCK1_LEVEL1 "\x02\x00\x00\x71\x01\x00\x64\x00\xfe"
+#define WM_IR_BLOCK2_LEVEL1 "\xfd\x05"
+#define WM_IR_BLOCK1_LEVEL2 "\x02\x00\x00\x71\x01\x00\x96\x00\xb4"
+#define WM_IR_BLOCK2_LEVEL2 "\xb3\x04"
+#define WM_IR_BLOCK1_LEVEL3 "\x02\x00\x00\x71\x01\x00\xaa\x00\x64"
+#define WM_IR_BLOCK2_LEVEL3 "\x63\x03"
+#define WM_IR_BLOCK1_LEVEL4 "\x02\x00\x00\x71\x01\x00\xc8\x00\x36"
+#define WM_IR_BLOCK2_LEVEL4 "\x35\x03"
+#define WM_IR_BLOCK1_LEVEL5 "\x07\x00\x00\x71\x01\x00\x72\x00\x20"
+#define WM_IR_BLOCK2_LEVEL5 "\x1f\x03"
+
+#define WM_IR_TYPE_BASIC 0x01
+#define WM_IR_TYPE_EXTENDED 0x03
+
+/* controller status flags for the first message byte */
+/* bit 1 is unknown */
+#define WM_CTRL_STATUS_BYTE1_ATTACHMENT 0x02
+#define WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED 0x04
+#define WM_CTRL_STATUS_BYTE1_IR_ENABLED 0x08
+#define WM_CTRL_STATUS_BYTE1_LED_1 0x10
+#define WM_CTRL_STATUS_BYTE1_LED_2 0x20
+#define WM_CTRL_STATUS_BYTE1_LED_3 0x40
+#define WM_CTRL_STATUS_BYTE1_LED_4 0x80
+
+/* aspect ratio */
+#define WM_ASPECT_16_9_X 660
+#define WM_ASPECT_16_9_Y 370
+#define WM_ASPECT_4_3_X 560
+#define WM_ASPECT_4_3_Y 420
+
+
+/**
+ * Expansion stuff
+ */
+
+/* encrypted expansion id codes (located at 0x04A400FC) */
+#define EXP_ID_CODE_NUNCHUK 0x9A1EFEFE
+#define EXP_ID_CODE_CLASSIC_CONTROLLER 0x9A1EFDFD
+#define EXP_ID_CODE_GUITAR 0x9A1EFDFB
+
+#define EXP_HANDSHAKE_LEN 224
+
+/********************
+ *
+ * End Wiimote internal codes
+ *
+ ********************/
+
+/* wiimote state flags - (some duplicated in wiiuse.h)*/
+#define WIIMOTE_STATE_DEV_FOUND 0x0001
+#define WIIMOTE_STATE_HANDSHAKE 0x0002 /* actual connection exists but no handshake yet */
+#define WIIMOTE_STATE_HANDSHAKE_COMPLETE 0x0004 /* actual connection exists but no handshake yet */
+#define WIIMOTE_STATE_CONNECTED 0x0008
+#define WIIMOTE_STATE_RUMBLE 0x0010
+#define WIIMOTE_STATE_ACC 0x0020
+#define WIIMOTE_STATE_EXP 0x0040
+#define WIIMOTE_STATE_IR 0x0080
+#define WIIMOTE_STATE_SPEAKER 0x0100
+#define WIIMOTE_STATE_IR_SENS_LVL1 0x0200
+#define WIIMOTE_STATE_IR_SENS_LVL2 0x0400
+#define WIIMOTE_STATE_IR_SENS_LVL3 0x0800
+#define WIIMOTE_STATE_IR_SENS_LVL4 0x1000
+#define WIIMOTE_STATE_IR_SENS_LVL5 0x2000
+
+#define WIIMOTE_INIT_STATES (WIIMOTE_STATE_IR_SENS_LVL3)
+
+/* macro to manage states */
+#define WIIMOTE_IS_SET(wm, s) ((wm->state & (s)) == (s))
+#define WIIMOTE_ENABLE_STATE(wm, s) (wm->state |= (s))
+#define WIIMOTE_DISABLE_STATE(wm, s) (wm->state &= ~(s))
+#define WIIMOTE_TOGGLE_STATE(wm, s) ((wm->state & (s)) ? WIIMOTE_DISABLE_STATE(wm, s) : WIIMOTE_ENABLE_STATE(wm, s))
+
+#define WIIMOTE_IS_FLAG_SET(wm, s) ((wm->flags & (s)) == (s))
+#define WIIMOTE_ENABLE_FLAG(wm, s) (wm->flags |= (s))
+#define WIIMOTE_DISABLE_FLAG(wm, s) (wm->flags &= ~(s))
+#define WIIMOTE_TOGGLE_FLAG(wm, s) ((wm->flags & (s)) ? WIIMOTE_DISABLE_FLAG(wm, s) : WIIMOTE_ENABLE_FLAG(wm, s))
+
+#define NUNCHUK_IS_FLAG_SET(wm, s) ((*(wm->flags) & (s)) == (s))
+
+/* misc macros */
+#define WIIMOTE_ID(wm) (wm->unid)
+#define WIIMOTE_IS_CONNECTED(wm) (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_CONNECTED))
+
+/*
+ * Smooth tilt calculations are computed with the
+ * exponential moving average formula:
+ * St = St_last + (alpha * (tilt - St_last))
+ * alpha is between 0 and 1
+ */
+#define WIIUSE_DEFAULT_SMOOTH_ALPHA 0.07f
+
+#define SMOOTH_ROLL 0x01
+#define SMOOTH_PITCH 0x02
+
+#include "wiiuse.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* not part of the api */
+int wiiuse_set_report_type(struct wiimote_t* wm);
+void wiiuse_send_next_pending_read_request(struct wiimote_t* wm);
+int wiiuse_send(struct wiimote_t* wm, byte report_type, byte* msg, int len);
+int wiiuse_read_data_cb(struct wiimote_t* wm, wiiuse_read_cb read_cb, byte* buffer, unsigned int offset, unsigned short len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WIIUSE_INTERNAL_H_INCLUDED */
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/wiiuse.cbp b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/wiiuse.cbp
new file mode 100644
index 0000000000..a71d3a0ceb
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/wiiuse.cbp
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_project_file>
+ <FileVersion major="1" minor="6" />
+ <Project>
+ <Option title="wiiuse" />
+ <Option makefile_is_custom="1" />
+ <Option pch_mode="2" />
+ <Option compiler="gcc" />
+ <MakeCommands>
+ <Build command="$make $target" />
+ <CompileFile command="$make $file" />
+ <Clean command="$make clean" />
+ <DistClean command="$make distclean" />
+ </MakeCommands>
+ <Build>
+ <Target title="Release">
+ <Option output="bin/Release/wiiuse" prefix_auto="1" extension_auto="1" />
+ <Option object_output="obj/Release/" />
+ <Option type="1" />
+ <Option compiler="gcc" />
+ <Compiler>
+ <Add option="-O2" />
+ </Compiler>
+ <Linker>
+ <Add option="-s" />
+ </Linker>
+ </Target>
+ </Build>
+ <Compiler>
+ <Add option="-Wall" />
+ </Compiler>
+ <Unit filename="CHANGELOG" />
+ <Unit filename="README" />
+ <Unit filename="example-sdl/sdl.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="example/example.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="src/classic.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="src/classic.h" />
+ <Unit filename="src/definitions.h" />
+ <Unit filename="src/dynamics.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="src/dynamics.h" />
+ <Unit filename="src/events.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="src/events.h" />
+ <Unit filename="src/guitar_hero_3.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="src/guitar_hero_3.h" />
+ <Unit filename="src/io.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="src/io.h" />
+ <Unit filename="src/io_nix.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="src/io_win.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="src/ir.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="src/ir.h" />
+ <Unit filename="src/nunchuk.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="src/nunchuk.h" />
+ <Unit filename="src/os.h" />
+ <Unit filename="src/wiiuse.c">
+ <Option compilerVar="CC" />
+ </Unit>
+ <Unit filename="src/wiiuse.h" />
+ <Unit filename="src/wiiuse_internal.h" />
+ <Extensions>
+ <code_completion />
+ <debugger />
+ </Extensions>
+ </Project>
+</CodeBlocks_project_file>
diff --git a/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/wiiuse.layout b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/wiiuse.layout
new file mode 100644
index 0000000000..5b74afea49
--- /dev/null
+++ b/tools/EventClients/Clients/WiiRemote/wiiuse_v0.12/wiiuse.layout
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<CodeBlocks_layout_file>
+ <ActiveTarget name="Release" />
+ <File name="CHANGELOG" open="1" top="0" tabpos="7">
+ <Cursor position="0" topLine="0" />
+ </File>
+ <File name="README" open="0" top="0" tabpos="8">
+ <Cursor position="3582" topLine="0" />
+ </File>
+ <File name="example-sdl/sdl.c" open="1" top="0" tabpos="6">
+ <Cursor position="3235" topLine="105" />
+ </File>
+ <File name="example/example.c" open="1" top="0" tabpos="5">
+ <Cursor position="0" topLine="0" />
+ </File>
+ <File name="src/classic.c" open="0" top="0" tabpos="4">
+ <Cursor position="2464" topLine="53" />
+ </File>
+ <File name="src/definitions.h" open="1" top="0" tabpos="9">
+ <Cursor position="1099" topLine="12" />
+ </File>
+ <File name="src/dynamics.c" open="0" top="0" tabpos="10">
+ <Cursor position="3216" topLine="59" />
+ </File>
+ <File name="src/events.c" open="1" top="0" tabpos="4">
+ <Cursor position="2983" topLine="117" />
+ </File>
+ <File name="src/guitar_hero_3.c" open="0" top="0" tabpos="0">
+ <Cursor position="2604" topLine="45" />
+ </File>
+ <File name="src/io.c" open="0" top="0" tabpos="6">
+ <Cursor position="1817" topLine="60" />
+ </File>
+ <File name="src/io_nix.c" open="0" top="0" tabpos="8">
+ <Cursor position="6286" topLine="211" />
+ </File>
+ <File name="src/io_win.c" open="0" top="0" tabpos="9">
+ <Cursor position="4021" topLine="123" />
+ </File>
+ <File name="src/ir.c" open="1" top="0" tabpos="8">
+ <Cursor position="0" topLine="0" />
+ </File>
+ <File name="src/ir.h" open="0" top="0" tabpos="0">
+ <Cursor position="1059" topLine="0" />
+ </File>
+ <File name="src/nunchuk.c" open="0" top="0" tabpos="10">
+ <Cursor position="2454" topLine="43" />
+ </File>
+ <File name="src/nunchuk.h" open="0" top="0" tabpos="0">
+ <Cursor position="1171" topLine="0" />
+ </File>
+ <File name="src/wiiuse.c" open="1" top="0" tabpos="1">
+ <Cursor position="0" topLine="0" />
+ </File>
+ <File name="src/wiiuse.h" open="1" top="1" tabpos="2">
+ <Cursor position="0" topLine="0" />
+ </File>
+ <File name="src/wiiuse_internal.h" open="1" top="0" tabpos="3">
+ <Cursor position="3424" topLine="81" />
+ </File>
+</CodeBlocks_layout_file>
diff --git a/tools/EventClients/Clients/XBMC Send/xbmc-send.py b/tools/EventClients/Clients/XBMC Send/xbmc-send.py
new file mode 100644
index 0000000000..2746ae026a
--- /dev/null
+++ b/tools/EventClients/Clients/XBMC Send/xbmc-send.py
@@ -0,0 +1,61 @@
+#!/usr/bin/python
+
+import sys
+import getopt
+from socket import *
+try:
+ from xbmc.xbmcclient import *
+except:
+ sys.path.append('../../lib/python')
+ from xbmcclient import *
+
+def usage():
+ print "xbmc-send [OPTION] --action=ACTION"
+ print 'Example'
+ print '\txbmc-send --host=192.168.0.1 --port=9777 --action="XBMC.Quit"'
+ print "Options"
+ print "\t-?, --help\t\t\tWill bring up this message"
+ print "\t--host=HOST\t\t\tChoose what HOST to connect to (default=localhost)"
+ print "\t--port=PORT\t\t\tChoose what PORT to connect to (default=9777)"
+ print '\t--action=ACTION\t\t\tSends an action to XBMC, this option can be added multiple times to create a macro'
+ pass
+
+def main():
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "?pa:v", ["help", "host=", "port=", "action="])
+ except getopt.GetoptError, err:
+ # print help information and exit:
+ print str(err) # will print something like "option -a not recognized"
+ usage()
+ sys.exit(2)
+ ip = "localhost"
+ port = 9777
+ actions = []
+ verbose = False
+ for o, a in opts:
+ if o in ("-?", "--help"):
+ usage()
+ sys.exit()
+ elif o == "--host":
+ ip = a
+ elif o == "--port":
+ port = int(a)
+ elif o in ("-a", "--action"):
+ actions.append(a)
+ else:
+ assert False, "unhandled option"
+
+ addr = (ip, port)
+ sock = socket(AF_INET,SOCK_DGRAM)
+
+ if len(actions) is 0:
+ usage()
+ sys.exit(0)
+
+ for action in actions:
+ print 'Sending action:', action
+ packet = PacketACTION(actionmessage=action, actiontype=ACTION_BUTTON)
+ packet.send(sock, addr)
+
+if __name__=="__main__":
+ main()
diff --git a/tools/EventClients/Clients/Xbox360 Controller/Xbox360Controller.cpp b/tools/EventClients/Clients/Xbox360 Controller/Xbox360Controller.cpp
new file mode 100644
index 0000000000..149026776e
--- /dev/null
+++ b/tools/EventClients/Clients/Xbox360 Controller/Xbox360Controller.cpp
@@ -0,0 +1,154 @@
+#include "StdAfx.h"
+#include "Xbox360Controller.h"
+
+
+Xbox360Controller::Xbox360Controller(int num)
+{
+ this->num = num;
+ for (int i = 0; i < 14; i++)
+ {
+ button_down[i] = false;
+ button_released[i] = false;
+ button_pressed[i] = false;
+ }
+}
+
+XINPUT_STATE Xbox360Controller::getState()
+{
+ // Zeroise the state
+ ZeroMemory(&state, sizeof(XINPUT_STATE));
+
+ // Get the state
+ XInputGetState(num, &state);
+
+ return state;
+}
+
+void Xbox360Controller::updateButton(int num, int button)
+{
+ if (state.Gamepad.wButtons & button)
+ {
+ if (!button_down[num])
+ {
+ button_pressed[num] = true;
+ }
+ button_down[num] = true;
+ } else
+ {
+ if (button_down[num])
+ {
+ button_released[num] = true;
+ }
+ button_down[num] = false;
+ }
+}
+
+bool Xbox360Controller::buttonPressed(int num)
+{
+ return button_pressed[num];
+}
+
+bool Xbox360Controller::buttonReleased(int num)
+{
+ return button_released[num];
+}
+
+void Xbox360Controller::updateState()
+{
+ for (int i = 0; i < 14; i++)
+ {
+ button_released[i] = false;
+ button_pressed[i] = false;
+ }
+ if (isConnected())
+ {
+ XINPUT_STATE s = getState();
+ updateButton(0, XINPUT_GAMEPAD_A);
+ updateButton(1, XINPUT_GAMEPAD_B);
+ updateButton(2, XINPUT_GAMEPAD_X);
+ updateButton(3, XINPUT_GAMEPAD_Y);
+ updateButton(4, XINPUT_GAMEPAD_DPAD_UP);
+ updateButton(5, XINPUT_GAMEPAD_DPAD_DOWN);
+ updateButton(6, XINPUT_GAMEPAD_DPAD_LEFT);
+ updateButton(7, XINPUT_GAMEPAD_DPAD_RIGHT);
+ updateButton(8, XINPUT_GAMEPAD_START);
+ updateButton(9, XINPUT_GAMEPAD_BACK);
+ updateButton(10, XINPUT_GAMEPAD_LEFT_THUMB);
+ updateButton(11, XINPUT_GAMEPAD_RIGHT_THUMB);
+ updateButton(12, XINPUT_GAMEPAD_LEFT_SHOULDER);
+ updateButton(13, XINPUT_GAMEPAD_RIGHT_SHOULDER);
+ }
+}
+
+bool Xbox360Controller::isConnected()
+{
+ // Zeroise the state
+ ZeroMemory(&state, sizeof(XINPUT_STATE));
+
+ // Get the state
+ DWORD Result = XInputGetState(num, &state);
+
+ if(Result == ERROR_SUCCESS)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+bool Xbox360Controller::triggerMoved(int num)
+{
+ if (num == 0)
+ return (state.Gamepad.bRightTrigger &&
+ state.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD);
+ return (state.Gamepad.bLeftTrigger &&
+ state.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD);
+}
+
+BYTE Xbox360Controller::getTrigger(int num)
+{
+ if (num == 0)
+ return state.Gamepad.bRightTrigger;
+ return state.Gamepad.bLeftTrigger;
+}
+
+bool Xbox360Controller::thumbMoved(int num)
+{
+ switch(num)
+ {
+ case 0:
+ return !(state.Gamepad.sThumbLX < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
+ state.Gamepad.sThumbLX > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
+ case 1:
+ return !(state.Gamepad.sThumbLY < XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE &&
+ state.Gamepad.sThumbLY > -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
+ case 2:
+ return !(state.Gamepad.sThumbRX < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE &&
+ state.Gamepad.sThumbRX > -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
+ case 3:
+ return !(state.Gamepad.sThumbRY < XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE &&
+ state.Gamepad.sThumbRY > -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
+ }
+
+ return false;
+}
+SHORT Xbox360Controller::getThumb(int num)
+{
+ switch (num)
+ {
+ case 0:
+ return state.Gamepad.sThumbLX;
+ case 1:
+ return state.Gamepad.sThumbLY;
+ case 2:
+ return state.Gamepad.sThumbRX;
+ case 3:
+ return state.Gamepad.sThumbRY;
+ }
+
+ return 0;
+}
+
diff --git a/tools/EventClients/Clients/Xbox360 Controller/Xbox360Controller.h b/tools/EventClients/Clients/Xbox360 Controller/Xbox360Controller.h
new file mode 100644
index 0000000000..5a858d5f80
--- /dev/null
+++ b/tools/EventClients/Clients/Xbox360 Controller/Xbox360Controller.h
@@ -0,0 +1,28 @@
+#pragma once
+#include <windows.h>
+#include <XInput.h> // Defines XBOX controller API
+#pragma comment(lib, "XInput.lib") // Library containing necessary 360
+ // functions
+
+class Xbox360Controller
+{
+private:
+ XINPUT_STATE state;
+ int num;
+ bool button_down[14];
+ bool button_pressed[14];
+ bool button_released[14];
+
+ XINPUT_STATE getState();
+ void updateButton(int num, int button);
+public:
+ Xbox360Controller(int num);
+ void updateState();
+ bool isConnected();
+ bool buttonPressed(int num);
+ bool buttonReleased(int num);
+ bool thumbMoved(int num);
+ SHORT getThumb(int num);
+ bool triggerMoved(int num);
+ BYTE getTrigger(int num);
+};
diff --git a/tools/EventClients/Clients/Xbox360 Controller/Xbox360EventClient.cpp b/tools/EventClients/Clients/Xbox360 Controller/Xbox360EventClient.cpp
new file mode 100644
index 0000000000..74b5e6a680
--- /dev/null
+++ b/tools/EventClients/Clients/Xbox360 Controller/Xbox360EventClient.cpp
@@ -0,0 +1,123 @@
+// Xbox360EventClient.cpp : Defines the entry point for the console application.
+//
+
+#include "stdafx.h"
+#include "Xbox360Controller.h"
+#include "../../lib/c++/xbmcclient.h"
+#pragma comment(lib, "wsock32.lib") // needed for xmbclient.h?
+
+// global variable :(
+// needed for exit event handler
+CXBMCClient *client;
+
+BOOL exitHandler( DWORD ctrlType )
+{
+ // TODO: Send BYE Packet
+ delete client;
+
+ WSACleanup();
+
+ return FALSE;
+}
+
+void checkTrigger(Xbox360Controller &cont, CXBMCClient *client, int num, const char* name)
+{
+ if (cont.triggerMoved(num))
+ {
+ client->SendButton(name, "XG", 0x20, cont.getTrigger(num) * 128);
+ }
+}
+
+void checkThumb(Xbox360Controller &cont, CXBMCClient *client, int num,
+ const char* leftname, const char* rightname)
+{
+ if (cont.thumbMoved(num))
+ {
+ if (cont.getThumb(num) < 0)
+ {
+ client->SendButton(leftname, "XG", 0x20, -cont.getThumb(num));
+ }
+ else
+ {
+ client->SendButton(rightname, "XG", 0x20, cont.getThumb(num));
+ }
+ }
+}
+
+void checkButton(Xbox360Controller &cont, CXBMCClient *client, int num, const char* name)
+{
+ if (cont.buttonPressed(num))
+ {
+ client->SendButton(name, "XG", 0x02);
+ }
+ else if (cont.buttonReleased(num))
+ {
+ client->SendButton(name, "XG", 0x04);
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ char *host = "localhost";
+ char *port = "9777";
+
+ Xbox360Controller cont(0);
+
+ // Start Winsock stuff
+ WSADATA wsaData;
+ WSAStartup(MAKEWORD(2, 2), &wsaData);
+
+ if ( argc > 3 )
+ {
+ printf("USAGE: %s [HOST [PORT]]\n\nThe event client connects to the XBMC EventServer at HOST:PORT.\
+ Default value for HOST is localhost, default value for port is 9777.\n", argv[0]);
+ return -1;
+ }
+
+ if ( argc > 1 )
+ {
+ host = argv[1];
+ }
+
+ if ( argc > 2 )
+ {
+ port = argv[2];
+ }
+
+ client = new CXBMCClient(host, atoi(port));
+
+ SetConsoleCtrlHandler( (PHANDLER_ROUTINE) exitHandler, TRUE);
+
+ client->SendHELO("Xbox 360 Controller", 0);
+
+ while(true)
+ {
+ if (cont.isConnected())
+ {
+ cont.updateState();
+ checkButton(cont, client, 0, "a");
+ checkButton(cont, client, 1, "b");
+ checkButton(cont, client, 2, "x");
+ checkButton(cont, client, 3, "y");
+ checkButton(cont, client, 4, "dpadup");
+ checkButton(cont, client, 5, "dpaddown");
+ checkButton(cont, client, 6, "dpadleft");
+ checkButton(cont, client, 7, "dpadright");
+ checkButton(cont, client, 8, "start");
+ checkButton(cont, client, 9, "back");
+ checkButton(cont, client, 10, "leftthumbbutton");
+ checkButton(cont, client, 11, "rightthumbbutton");
+ checkButton(cont, client, 12, "white");
+ checkButton(cont, client, 13, "black");
+ checkTrigger(cont, client, 0, "rightanalogtrigger");
+ checkTrigger(cont, client, 1, "leftanalogtrigger");
+ checkThumb(cont, client, 0, "leftthumbstickleft", "leftthumbstickright");
+ checkThumb(cont, client, 1, "leftthumbstickdown", "leftthumbstickup");
+ checkThumb(cont, client, 2, "rightthumbstickleft", "rightthumbstickright");
+ checkThumb(cont, client, 3, "rightthumbstickdown", "rightthumbstickup");
+ }
+ Sleep(10);
+ }
+
+ return 0;
+}
diff --git a/tools/EventClients/Clients/Xbox360 Controller/Xbox360EventClient.vcproj b/tools/EventClients/Clients/Xbox360 Controller/Xbox360EventClient.vcproj
new file mode 100644
index 0000000000..4e6f8c558e
--- /dev/null
+++ b/tools/EventClients/Clients/Xbox360 Controller/Xbox360EventClient.vcproj
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="Xbox360EventClient"
+ ProjectGUID="{19E7A234-4F29-4828-BBC7-E3564AB57EF0}"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="0"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="Debug"
+ IntermediateDirectory="Debug"
+ ConfigurationType="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="Release"
+ IntermediateDirectory="Release"
+ ConfigurationType="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;"
+ RuntimeLibrary="2"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ <File
+ RelativePath=".\targetver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\Xbox360Controller.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\stdafx.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\Xbox360Controller.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\Xbox360EventClient.cpp"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/tools/EventClients/Clients/Xbox360 Controller/stdafx.cpp b/tools/EventClients/Clients/Xbox360 Controller/stdafx.cpp
new file mode 100644
index 0000000000..81d9f220d5
--- /dev/null
+++ b/tools/EventClients/Clients/Xbox360 Controller/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// Xbox360EventClient.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/tools/EventClients/Clients/Xbox360 Controller/stdafx.h b/tools/EventClients/Clients/Xbox360 Controller/stdafx.h
new file mode 100644
index 0000000000..47a0d0252b
--- /dev/null
+++ b/tools/EventClients/Clients/Xbox360 Controller/stdafx.h
@@ -0,0 +1,15 @@
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#include <stdio.h>
+#include <tchar.h>
+
+
+
+// TODO: reference additional headers your program requires here
diff --git a/tools/EventClients/Clients/Xbox360 Controller/targetver.h b/tools/EventClients/Clients/Xbox360 Controller/targetver.h
new file mode 100644
index 0000000000..a38195a4ef
--- /dev/null
+++ b/tools/EventClients/Clients/Xbox360 Controller/targetver.h
@@ -0,0 +1,13 @@
+#pragma once
+
+// The following macros define the minimum required platform. The minimum required platform
+// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run
+// your application. The macros work by enabling all features available on platform versions up to and
+// including the version specified.
+
+// Modify the following defines if you have to target a platform prior to the ones specified below.
+// Refer to MSDN for the latest info on corresponding values for different platforms.
+#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista.
+#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows.
+#endif
+
diff --git a/tools/EventClients/Makefile b/tools/EventClients/Makefile
new file mode 100644
index 0000000000..5e902f7fbc
--- /dev/null
+++ b/tools/EventClients/Makefile
@@ -0,0 +1,32 @@
+# Default prefix dir
+prefix=/usr/local
+installdir=$(prefix)
+WII_EXTRA_OPTS=
+
+all: wiimote install
+
+wiimote:
+ cd Clients/WiiRemote && \
+ g++ CWIID_WiiRemote.cpp -lcwiid -DICON_PATH="\"$(installdir)/share/pixmaps/xbmc/\"" $(WII_EXTRA_OPTS) -o WiiRemote
+
+j2me-remote:
+ cd Clients/J2ME\ Client
+ ant -f build.xml
+
+install:
+ mkdir -p $(prefix)/bin
+ cp -a Clients/WiiRemote/WiiRemote $(prefix)/bin/xbmc-wiiremote
+ cp -a Clients/J2ME\ Client/j2me_remote.py $(prefix)/bin/xbmc-j2meremote
+ cp -a Clients/PS3\ BD\ Remote/ps3_remote.py $(prefix)/bin/xbmc-ps3remote
+ #cp -a Clients/PS3\ Sixaxis\ Controller/ps3d.py $(prefix)/bin/xbmc-ps3d
+ mkdir -p $(prefix)/lib/python2.5/site-packages/xbmc
+ echo 'ICON_PATH="$(installdir)/share/pixmaps/xbmc/"' > $(prefix)/lib/python2.5/site-packages/xbmc/defs.py
+ cp -a lib/python/* $(prefix)/lib/python2.5/site-packages/xbmc/
+ cp -a Clients/PS3\ BD\ Remote/ps3_remote.py $(prefix)/lib/python2.5/site-packages/xbmc/
+ mkdir -p $(prefix)/include/xbmc
+ cp -a lib/c++/* $(prefix)/include/xbmc/
+ mkdir -p $(prefix)/share/pixmaps/xbmc
+ cp -a icons/* $(prefix)/share/pixmaps/xbmc/
+
+clean:
+ rm -f Clients/WiiRemote/WiiRemote
diff --git a/tools/EventClients/README.txt b/tools/EventClients/README.txt
new file mode 100644
index 0000000000..6e1a454e5b
--- /dev/null
+++ b/tools/EventClients/README.txt
@@ -0,0 +1,101 @@
+Event Client Examples and PS3 Sixaxis and Blu-Ray Remote Support
+----------------------------------------------------------------
+
+This directory contains 6 sample programs that demonstrate XBMC's
+event server (which is still in development). The programs are in
+Python and C++. XBMC also needs to be running (obviously) so that it
+can receive the events.
+
+- example_button1.py | example_button1.cpp
+- example_button2.py | example_button2.cpp
+- example_notification.py | example_notification.cpp
+
+The first 2 show how button / key presses can be sent to XBMC.
+The third one shows how to display notifications in XBMC.
+
+- xbmcclient.py
+- xbmcclient.h
+
+These are the Python module and C++ header that you can use when
+writing your own programs. It is not yet complete and likely to change
+but is still usable.
+
+Implementation details can be found in the comments in the sample
+programs.
+
+
+PS3 Controller and PS3 Blu-Ray Remote Support
+---------------------------------------------
+
+There is also initial support for the PS3 controller (sixaxis) and the
+PS3 Blu-Ray remote.
+
+Pairing of the PS3 Blu-Ray Remote
+---------------------------------
+
+The remote needs to be paired initially with the 'ps3_remote.py'
+program in this directory which you can continue using if you do not
+want to run 'ps3d.py' as root. The disadvantage of using
+'ps3_remote.py' is that pairing is required on every run. Once initial
+pairing is done, 'ps3d.py', when run as root, will automatically
+detect incoming connections from both the PS3 remote and the Sixaxis
+controller.
+
+Pairing of the PS3 Sixaxis Controller (TODO)
+--------------------------------------------
+
+The pairing of the PS3 controller is not yet handled automatically. It
+can however be done using the program "sixaxis.c" available from:
+
+http://www.pabr.org/sixlinux/sixlinux.en.html
+
+Once pairing for eiher or both has been done, run the ps3d.py program
+as root after disabling any existing HID servers that might currently
+be running. The program requires root prvilieges since it listens on
+Bluetooth L2CAP PSMs 17 and 19.
+
+Using the PS3 Sixaxis Controller
+--------------------------------
+
+Currently, all that is supported with the Sixaxis controller is to be able
+emulate the mouse behavior. Hold down the PS button and wave the controller
+around and watch the mouse in XBMC mouse. Tilt it from left to right (along
+your Z axis) to control horizontal motion. Tilt it towards and away from you
+along (along your X axis) to control vertical mouse movement.
+
+That's all for now.
+
+WiiRemote Support
+-----------------
+
+The executable depends on libcwiid and libbluetooth and is compiled using
+# g++ WiiRemote.cpp -lcwiid -o WiiRemote
+The WiiRemote will emulate mouse by default but can be disabled by running with --disable-mouseemulation
+The sensitivity of the mouseemulation can be set using the --deadzone_x or --deadzone_y where the number is
+the percentage of the space is considered "dead", higher means more sensative.
+Other commands can be listed with --help
+
+The WiiRemote is mappable with keymap.xml where button id's are the following:
+1 = Up
+2 = Down
+3 = Left
+4 = Right
+5 = A
+6 = B
+7 = Minus
+8 = Home
+9 = Plus
+10 = 1
+11 = 2
+The name is by standard WiiRemote but this can be changed with the --joystick-name
+
+J2ME (Java Phone Application)
+-----------------------------
+
+To use the J2ME client only CLDC 1.0 and MIDP 1.0 is needed.
+The client will also need bluetooth and must be able to initialize the connection.
+For compilation of the Java Application see Clients/J2ME Client/README but precompiled versions
+exists in our forum.
+
+The Client is mappable in the same manner as PS3 in keymap.xml but with the name J2ME (<joystick name="J2ME">).
+The KeyID's generated in terminal when running j2me_remote.py.
diff --git a/tools/EventClients/examples/c#/XBMCDemoClient1.cs b/tools/EventClients/examples/c#/XBMCDemoClient1.cs
new file mode 100644
index 0000000000..849cc36c07
--- /dev/null
+++ b/tools/EventClients/examples/c#/XBMCDemoClient1.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+using XBMC;
+
+namespace XBMCEventClientDemo
+{
+ static class Program
+ {
+ /// <summary>
+ /// The main entry point for the application.
+ /// </summary>
+ [STAThread]
+ static void Main()
+ {
+ EventClient eventClient = new EventClient();
+ eventClient.Connect("127.0.0.1", 9777);
+ eventClient.SendHelo("XBMC Client Demo", IconType.ICON_PNG, "icon.png");
+ System.Threading.Thread.Sleep(1000);
+ eventClient.SendNotification("XBMC Client Demo", "Notification Message", IconType.ICON_PNG, "/usr/share/xbmc/media/icon.png");
+ System.Threading.Thread.Sleep(1000);
+ eventClient.SendButton("dpadup", "XG", ButtonFlagsType.BTN_DOWN | ButtonFlagsType.BTN_NO_REPEAT, 0);
+ System.Threading.Thread.Sleep(1000);
+ eventClient.SendPing();
+ System.Threading.Thread.Sleep(1000);
+ eventClient.SendMouse(32768, 32768);
+ System.Threading.Thread.Sleep(1000);
+ eventClient.SendLog(LogTypeEnum.LOGERROR, "Example error log message from XBMC Client Demo");
+ System.Threading.Thread.Sleep(1000);
+ eventClient.SendAction("Mute");
+ System.Threading.Thread.Sleep(1000);
+ eventClient.SendBye();
+ }
+ }
+}
diff --git a/tools/EventClients/examples/c++/example_button1.cpp b/tools/EventClients/examples/c++/example_button1.cpp
new file mode 100644
index 0000000000..a980e3da24
--- /dev/null
+++ b/tools/EventClients/examples/c++/example_button1.cpp
@@ -0,0 +1,38 @@
+#include "../../lib/c++/xbmcclient.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+
+int main(int argc, char **argv)
+{
+ /* connect to localhost, port 9777 using a UDP socket
+ this only needs to be done once.
+ by default this is where XBMC will be listening for incoming
+ connections. */
+ CAddress my_addr; // Address => localhost on 9777
+ int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ {
+ printf("Error creating socket\n");
+ return -1;
+ }
+
+ my_addr.Bind(sockfd);
+
+ CPacketHELO HeloPackage("Example Remote", ICON_PNG, "../../icons/bluetooth.png");
+ HeloPackage.Send(sockfd, my_addr);
+
+ sleep(5);
+ // press 'S'
+ CPacketBUTTON btn1('S', true);
+ btn1.Send(sockfd, my_addr);
+
+ sleep(2);
+ // press the enter key (13 = enter)
+ CPacketBUTTON btn2(13, true);
+ btn2.Send(sockfd, my_addr);
+
+ // BYE is not required since XBMC would have shut down
+ CPacketBYE bye; // CPacketPing if you want to ping
+ bye.Send(sockfd, my_addr);
+}
diff --git a/tools/EventClients/examples/c++/example_button2.cpp b/tools/EventClients/examples/c++/example_button2.cpp
new file mode 100644
index 0000000000..852e49f9a2
--- /dev/null
+++ b/tools/EventClients/examples/c++/example_button2.cpp
@@ -0,0 +1,48 @@
+#include "../../lib/c++/xbmcclient.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+
+int main(int argc, char **argv)
+{
+ /* connect to localhost, port 9777 using a UDP socket
+ this only needs to be done once.
+ by default this is where XBMC will be listening for incoming
+ connections. */
+ CAddress my_addr; // Address => localhost on 9777
+ int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ {
+ printf("Error creating socket\n");
+ return -1;
+ }
+
+ my_addr.Bind(sockfd);
+
+ CPacketHELO HeloPackage("Example Remote", ICON_PNG, "../../icons/bluetooth.png");
+ HeloPackage.Send(sockfd, my_addr);
+
+ sleep(5);
+ // Note that we have foo(BUTTON, DEVICEMAP);
+ CPacketBUTTON btn1("dpadup", "XG");
+ btn1.Send(sockfd, my_addr);
+
+ sleep(5);
+
+ CPacketBUTTON btn2(0x28);
+ btn2.Send(sockfd, my_addr);
+
+ sleep(5);
+
+ CPacketBUTTON btn3("right", "KB");
+ btn3.Send(sockfd, my_addr);
+
+ sleep(5);
+ // Release button
+ CPacketBUTTON btn4;
+ btn4.Send(sockfd, my_addr);
+
+ // BYE is not required since XBMC would have shut down
+ CPacketBYE bye; // CPacketPing if you want to ping
+ bye.Send(sockfd, my_addr);
+}
diff --git a/tools/EventClients/examples/c++/example_log.cpp b/tools/EventClients/examples/c++/example_log.cpp
new file mode 100644
index 0000000000..6194ed9c51
--- /dev/null
+++ b/tools/EventClients/examples/c++/example_log.cpp
@@ -0,0 +1,34 @@
+#include "../../lib/c++/xbmcclient.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+
+int main(int argc, char **argv)
+{
+ /* connect to localhost, port 9777 using a UDP socket
+ this only needs to be done once.
+ by default this is where XBMC will be listening for incoming
+ connections. */
+
+ CAddress my_addr; // Address => localhost on 9777
+ int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ {
+ printf("Error creating socket\n");
+ return -1;
+ }
+
+ my_addr.Bind(sockfd);
+ //Normally this is already done by the client
+ CPacketHELO HeloPackage("LOG Test", ICON_NONE);
+ HeloPackage.Send(sockfd, my_addr);
+
+ sleep(5);
+ //This works as XBMC internal CLog::LOG(LOGTYPE, STRING);
+ CPacketLOG packet(LOGERROR, "The Log Message");
+ packet.Send(sockfd, my_addr);
+
+ // BYE is not required since XBMC would have shut down
+ CPacketBYE bye; // CPacketPing if you want to ping
+ bye.Send(sockfd, my_addr);
+}
diff --git a/tools/EventClients/examples/c++/example_mouse.cpp b/tools/EventClients/examples/c++/example_mouse.cpp
new file mode 100644
index 0000000000..23f40d3de8
--- /dev/null
+++ b/tools/EventClients/examples/c++/example_mouse.cpp
@@ -0,0 +1,36 @@
+#include "../../lib/c++/xbmcclient.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+
+int main(int argc, char **argv)
+{
+ /* connect to localhost, port 9777 using a UDP socket
+ this only needs to be done once.
+ by default this is where XBMC will be listening for incoming
+ connections. */
+ CAddress my_addr; // Address => localhost on 9777
+ int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ {
+ printf("Error creating socket\n");
+ return -1;
+ }
+
+ my_addr.Bind(sockfd);
+
+ CPacketHELO HeloPackage("Example Mouse", ICON_PNG, "../../icons/mouse.png");
+ HeloPackage.Send(sockfd, my_addr);
+
+ sleep(5);
+
+ for(int i = 0; i < 65536; i++)
+ {
+ CPacketMOUSE mouse(i,i);
+ mouse.Send(sockfd, my_addr);
+ }
+
+ // BYE is not required since XBMC would have shut down
+ CPacketBYE bye; // CPacketPing if you want to ping
+ bye.Send(sockfd, my_addr);
+}
diff --git a/tools/EventClients/examples/c++/example_notification.cpp b/tools/EventClients/examples/c++/example_notification.cpp
new file mode 100644
index 0000000000..dc51075b85
--- /dev/null
+++ b/tools/EventClients/examples/c++/example_notification.cpp
@@ -0,0 +1,37 @@
+#include "../../lib/c++/xbmcclient.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+
+int main(int argc, char **argv)
+{
+ /* connect to localhost, port 9777 using a UDP socket
+ this only needs to be done once.
+ by default this is where XBMC will be listening for incoming
+ connections. */
+
+ CAddress my_addr; // Address => localhost on 9777
+ int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ {
+ printf("Error creating socket\n");
+ return -1;
+ }
+
+ my_addr.Bind(sockfd);
+
+ CPacketHELO HeloPackage("Email Notifier", ICON_NONE);
+ HeloPackage.Send(sockfd, my_addr);
+
+ sleep(5);
+
+ CPacketNOTIFICATION packet("New Mail!", // caption
+ "RE: Check this out", // message
+ ICON_PNG, // optional icon type
+ "../../icons/mail.png"); // icon file (local)
+ packet.Send(sockfd, my_addr);
+
+ // BYE is not required since XBMC would have shut down
+ CPacketBYE bye; // CPacketPing if you want to ping
+ bye.Send(sockfd, my_addr);
+}
diff --git a/tools/EventClients/examples/java/XBMCDemoClient1.java b/tools/EventClients/examples/java/XBMCDemoClient1.java
new file mode 100644
index 0000000000..0be3865cbd
--- /dev/null
+++ b/tools/EventClients/examples/java/XBMCDemoClient1.java
@@ -0,0 +1,52 @@
+package org.xbmc.eventclient.demo;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+
+import org.xbmc.eventclient.XBMCClient;
+
+/**
+ * Simple Demo EventClient
+ * @author Stefan Agner
+ *
+ */
+public class XBMCDemoClient1 {
+
+ /**
+ * Simple Demo EventClient
+ * @param args
+ */
+ public static void main(String[] args) throws IOException, InterruptedException {
+ InetAddress host = Inet4Address.getByAddress(new byte[] { (byte)192, (byte)168, 0, 20 } );
+
+ Thread.sleep(20000);
+ XBMCClient oXBMCClient = new XBMCClient(host, 9777, "My Client", "/usr/share/xbmc/media/icon.png");
+
+ Thread.sleep(7000);
+
+ oXBMCClient.sendNotification("My Title", "My Message");
+
+
+ Thread.sleep(7000);
+
+ oXBMCClient.sendButton("KB", "escape", false, true, false, (short)0 , (byte)0);
+
+
+ Thread.sleep(7000);
+ oXBMCClient.sendButton("KB", "escape", true, true, false, (short)0 , (byte)0);
+ oXBMCClient.sendNotification("My Title", "Escape sent");
+
+ Thread.sleep(1000);
+
+ oXBMCClient.sendButton("KB", "escape", true, false, false, (short)0 , (byte)0);
+ oXBMCClient.sendNotification("My Title", "Escape released");
+
+ Thread.sleep(7000);
+ oXBMCClient.sendLog((byte)0, "My Client disconnects....");
+ oXBMCClient.sendNotification("My Title", "Client will disconnect");
+ oXBMCClient.stopClient();
+
+ }
+
+}
diff --git a/tools/EventClients/examples/python/example_action.py b/tools/EventClients/examples/python/example_action.py
new file mode 100755
index 0000000000..d5ab9cf61c
--- /dev/null
+++ b/tools/EventClients/examples/python/example_action.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+# This is a simple example showing how you can send a key press event
+# to XBMC using the XBMCClient class
+
+import sys
+sys.path.append("../../lib/python")
+
+import time
+from xbmcclient import XBMCClient,ACTION_EXECBUILTIN,ACTION_BUTTON
+
+def main():
+
+ host = "localhost"
+ port = 9777
+
+ # Create an XBMCClient object and connect
+ xbmc = XBMCClient("Example Remote", "../../icons/bluetooth.png")
+ xbmc.connect()
+
+ # send a up key press using the xbox gamepad map "XG" and button
+ # name "dpadup" ( see PacketBUTTON doc for more details)
+ try:
+ xbmc.send_action(sys.argv[2], ACTION_BUTTON)
+ except:
+ try:
+ xbmc.send_action(sys.argv[1], ACTION_EXECBUILTIN)
+ except Exception, e:
+ print str(e)
+ xbmc.send_action("XBMC.ActivateWindow(ShutdownMenu)")
+
+
+ # ok we're done, close the connection
+ # Note that closing the connection clears any repeat key that is
+ # active. So in this example, the actual release button event above
+ # need not have been sent.
+ xbmc.close()
+
+if __name__=="__main__":
+ main()
diff --git a/tools/EventClients/examples/python/example_button1.py b/tools/EventClients/examples/python/example_button1.py
new file mode 100755
index 0000000000..7b208252cb
--- /dev/null
+++ b/tools/EventClients/examples/python/example_button1.py
@@ -0,0 +1,79 @@
+#!/usr/bin/python
+
+# This is a simple example showing how you can send 2 button events
+# to XBMC in a queued fashion to shut it down.
+
+# Queued button events are not repeatable.
+
+# The basic idea is to create single packets and shoot them to XBMC
+# The provided library implements some of the support commands and
+# takes care of creating the actual packet. Using it is as simple
+# as creating an object with the required constructor arguments and
+# sending it through a socket.
+
+# Currently, only keyboard keys are supported so the key codes used
+# below are the same key codes used in guilib/common/SDLKeyboard.cpp
+
+# In effect, anything that can be done with the keyboard can be done
+# using the event client.
+
+# import the XBMC client library
+# NOTE: The library is not complete yet but is usable at this stage.
+
+import sys
+sys.path.append("../../lib/python")
+
+from xbmcclient import *
+from socket import *
+
+def main():
+ import time
+ import sys
+
+ # connect to localhost, port 9777 using a UDP socket
+ # this only needs to be done once.
+ # by default this is where XBMC will be listening for incoming
+ # connections.
+ host = "localhost"
+ port = 9777
+ addr = (host, port)
+ sock = socket(AF_INET,SOCK_DGRAM)
+
+ # First packet must be HELO (no it's not a typo) and can contain an icon
+ # 'icon_type' can be one of ICON_NONE, ICON_PNG, ICON_JPG or ICON_GIF
+ packet = PacketHELO(devicename="Example Remote",
+ icon_type=ICON_PNG,
+ icon_file="../../icons/bluetooth.png")
+ packet.send(sock, addr)
+
+ # IMPORTANT: After a HELO packet is sent, the client needs to "ping" XBMC
+ # at least once every 60 seconds or else the client will time out.
+ # Every valid packet sent to XBMC acts as a ping, however if no valid
+ # packets NEED to be sent (eg. the user hasn't pressed a key in 50 seconds)
+ # then you can use the PacketPING class to send a ping packet (which is
+ # basically just an emppty packet). See below.
+
+ # Once a client times out, it will need to reissue the HELO packet.
+ # Currently, since this is a unidirectional protocol, there is no way
+ # for the client to know if it has timed out.
+
+ # wait for notification window to close (in XBMC)
+ time.sleep(5)
+
+ # press 'S'
+ packet = PacketBUTTON(code='S', queue=1)
+ packet.send(sock, addr)
+
+ # wait for a few seconds
+ time.sleep(2)
+
+ # press the enter key (13 = enter)
+ packet = PacketBUTTON(code=13, queue=1)
+ packet.send(sock, addr)
+
+ # BYE is not required since XBMC would have shut down
+ packet = PacketBYE() # PacketPING if you want to ping
+ packet.send(sock, addr)
+
+if __name__=="__main__":
+ main()
diff --git a/tools/EventClients/examples/python/example_button2.py b/tools/EventClients/examples/python/example_button2.py
new file mode 100755
index 0000000000..b420bd84b8
--- /dev/null
+++ b/tools/EventClients/examples/python/example_button2.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+
+# This is a simple example showing how you can send a key press event
+# to XBMC in a non-queued fashion to achieve a button pressed down
+# event i.e. a key press that repeats.
+
+# The repeat interval is currently hard coded in XBMC but that might
+# change in the future.
+
+# NOTE: Read the comments in 'example_button1.py' for a more detailed
+# explanation.
+
+import sys
+sys.path.append("../../lib/python")
+
+from xbmcclient import *
+from socket import *
+
+def main():
+ import time
+ import sys
+
+ host = "localhost"
+ port = 9777
+ addr = (host, port)
+
+ sock = socket(AF_INET,SOCK_DGRAM)
+
+ # First packet must be HELO and can contain an icon
+ packet = PacketHELO("Example Remote", ICON_PNG,
+ "../../icons/bluetooth.png")
+ packet.send(sock, addr)
+
+ # wait for notification window to close (in XBMC)
+ time.sleep(5)
+
+ # send a up key press using the xbox gamepad map "XG" and button
+ # name "dpadup" ( see PacketBUTTON doc for more details)
+ packet = PacketBUTTON(map_name="XG", button_name="dpadup")
+ packet.send(sock, addr)
+
+ # wait for a few seconds to see its effect
+ time.sleep(5)
+
+ # send a down key press using the raw keyboard code
+ packet = PacketBUTTON(code=0x28)
+ packet.send(sock, addr)
+
+ # wait for a few seconds to see its effect
+ time.sleep(5)
+
+ # send a right key press using the keyboard map "KB" and button
+ # name "right"
+ packet = PacketBUTTON(map_name="KB", button_name="right")
+ packet.send(sock, addr)
+
+ # wait for a few seconds to see its effect
+ time.sleep(5)
+
+ # that's enough, release the button. During release, button code
+ # doesn't matter.
+ packet = PacketBUTTON(code=0x28, down=0)
+ packet.send(sock, addr)
+
+ # ok we're done, close the connection
+ # Note that closing the connection clears any repeat key that is
+ # active. So in this example, the actual release button event above
+ # need not have been sent.
+ packet = PacketBYE()
+ packet.send(sock, addr)
+
+if __name__=="__main__":
+ main()
diff --git a/tools/EventClients/examples/python/example_mouse.py b/tools/EventClients/examples/python/example_mouse.py
new file mode 100755
index 0000000000..10d7f61ba7
--- /dev/null
+++ b/tools/EventClients/examples/python/example_mouse.py
@@ -0,0 +1,45 @@
+#!/usr/bin/python
+
+# This is a simple example showing how you can send mouse movement
+# events to XBMC.
+
+# NOTE: Read the comments in 'example_button1.py' for a more detailed
+# explanation.
+
+import sys
+sys.path.append("../../lib/python")
+
+from xbmcclient import *
+from socket import *
+
+def main():
+ import time
+ import sys
+
+ host = "localhost"
+ port = 9777
+ addr = (host, port)
+
+ sock = socket(AF_INET,SOCK_DGRAM)
+
+ # First packet must be HELO and can contain an icon
+ packet = PacketHELO("Example Mouse", ICON_PNG,
+ "../../icons/mouse.png")
+ packet.send(sock, addr)
+
+ # wait for notification window to close (in XBMC)
+ time.sleep(2)
+
+ # send mouse events to take cursor from top left to bottom right of the screen
+ # here 0 to 65535 will map to XBMC's screen width and height.
+ # Specifying absolute mouse coordinates is unsupported currently.
+ for i in range(0, 65535, 2):
+ packet = PacketMOUSE(i,i)
+ packet.send(sock, addr)
+
+ # ok we're done, close the connection
+ packet = PacketBYE()
+ packet.send(sock, addr)
+
+if __name__=="__main__":
+ main()
diff --git a/tools/EventClients/examples/python/example_notification.py b/tools/EventClients/examples/python/example_notification.py
new file mode 100755
index 0000000000..81bbadccd2
--- /dev/null
+++ b/tools/EventClients/examples/python/example_notification.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+# This is a simple example showing how you can show a notification
+# window with a custom icon inside XBMC. It could be used by mail
+# monitoring apps, calendar apps, etc.
+
+import sys
+sys.path.append("../../lib/python")
+
+from xbmcclient import *
+from socket import *
+
+def main():
+ import time
+ import sys
+
+ host = "localhost"
+ port = 9777
+ addr = (host, port)
+ sock = socket(AF_INET,SOCK_DGRAM)
+
+ packet = PacketHELO("Email Notifier", ICON_NONE)
+ packet.send(sock, addr)
+
+ # wait for 5 seconds
+ time.sleep (5)
+
+ packet = PacketNOTIFICATION("New Mail!", # caption
+ "RE: Check this out", # message
+ ICON_PNG, # optional icon type
+ "../../icons/mail.png") # icon file (local)
+ packet.send(sock, addr)
+
+ packet = PacketBYE()
+ packet.send(sock, addr)
+
+if __name__=="__main__":
+ main()
diff --git a/tools/EventClients/examples/python/example_simple.py b/tools/EventClients/examples/python/example_simple.py
new file mode 100755
index 0000000000..6218131e13
--- /dev/null
+++ b/tools/EventClients/examples/python/example_simple.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python
+
+# This is a simple example showing how you can send a key press event
+# to XBMC using the XBMCClient class
+
+import sys
+sys.path.append("../../lib/python")
+
+import time
+from xbmcclient import XBMCClient
+
+def main():
+
+ host = "localhost"
+ port = 9777
+
+ # Create an XBMCClient object and connect
+ xbmc = XBMCClient("Example Remote", "../../icons/bluetooth.png")
+ xbmc.connect()
+
+ # wait for notification window to close (in XBMC) (optional)
+ time.sleep(5)
+
+ # send a up key press using the xbox gamepad map "XG" and button
+ # name "dpadup" ( see PacketBUTTON doc for more details)
+ xbmc.send_button(map="XG", button="dpadup")
+
+ # wait for a few seconds to see its effect
+ time.sleep(5)
+
+ # send a right key press using the keyboard map "KB" and button
+ # name "right"
+ xbmc.send_keyboard_button("right")
+
+ # wait for a few seconds to see its effect
+ time.sleep(5)
+
+ # that's enough, release the button.
+ xbmc.release_button()
+
+ # ok we're done, close the connection
+ # Note that closing the connection clears any repeat key that is
+ # active. So in this example, the actual release button event above
+ # need not have been sent.
+ xbmc.close()
+
+if __name__=="__main__":
+ main()
diff --git a/tools/EventClients/icons/bluetooth.png b/tools/EventClients/icons/bluetooth.png
new file mode 100644
index 0000000000..5ede31fd45
--- /dev/null
+++ b/tools/EventClients/icons/bluetooth.png
Binary files differ
diff --git a/tools/EventClients/icons/mail.png b/tools/EventClients/icons/mail.png
new file mode 100644
index 0000000000..7c68b2e854
--- /dev/null
+++ b/tools/EventClients/icons/mail.png
Binary files differ
diff --git a/tools/EventClients/icons/mouse.png b/tools/EventClients/icons/mouse.png
new file mode 100644
index 0000000000..c0e165760c
--- /dev/null
+++ b/tools/EventClients/icons/mouse.png
Binary files differ
diff --git a/tools/EventClients/icons/phone.png b/tools/EventClients/icons/phone.png
new file mode 100644
index 0000000000..3db1682eeb
--- /dev/null
+++ b/tools/EventClients/icons/phone.png
Binary files differ
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()
+