2 * Copyright (C) 2008-2013 Team XBMC
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 using System.Net.Sockets;
35 public enum ButtonFlagsType
40 BTN_USE_AMOUNT = 0x08,
47 public enum MouseFlagsType
52 public enum LogTypeEnum
64 public enum ActionType
66 ACTION_EXECBUILTIN = 0x01,
70 public class EventClient
73 /************************************************************************/
74 /* Written by Peter Tribe aka EqUiNox (TeamBlackbolt) */
75 /* Based upon XBMC's xbmcclient.cpp class */
76 /************************************************************************/
78 private enum PacketType
85 PT_BROADCAST = 0x06, //Currently not implemented
86 PT_NOTIFICATION = 0x07,
90 PT_DEBUG = 0xFF //Currently not implemented
93 private const int STD_PORT = 9777;
94 private const int MAX_PACKET_SIZE = 1024;
95 private const int HEADER_SIZE = 32;
96 private const int MAX_PAYLOAD_SIZE = MAX_PACKET_SIZE - HEADER_SIZE;
97 private const byte MAJOR_VERSION = 2;
98 private const byte MINOR_VERSION = 0;
100 private uint uniqueToken;
101 private Socket socket;
103 public bool Connect(string Address)
105 return Connect(Address, STD_PORT, (uint)System.DateTime.Now.TimeOfDay.Milliseconds);
108 public bool Connect(string Address, int Port)
110 return Connect(Address, Port, (uint)System.DateTime.Now.TimeOfDay.Milliseconds);
114 public bool Connect(string Address, int Port, uint UID)
118 if (socket != null) Disconnect();
120 socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
122 // For compilation with .net framework 1.0 or 1.1
123 // define the FRAMEWORK_1_x conditional compilation constant in the project
124 // or add /define:FRAMEWORK_1_x to the csc.exe command line
126 socket.Connect(Dns.GetHostByName(Address).AddressList[0].ToString(), Port);
128 socket.Connect(Address, Port);
138 public bool Connected
142 if (socket == null) return false;
143 return socket.Connected;
147 public void Disconnect()
153 socket.Shutdown(SocketShutdown.Both);
163 private byte[] Header(PacketType PacketType, int NumberOfPackets, int CurrentPacket, int PayloadSize)
166 byte[] header = new byte[HEADER_SIZE];
168 header[0] = (byte)'X';
169 header[1] = (byte)'B';
170 header[2] = (byte)'M';
171 header[3] = (byte)'C';
173 header[4] = MAJOR_VERSION;
174 header[5] = MINOR_VERSION;
176 if (CurrentPacket == 1)
178 header[6] = (byte)(((ushort)PacketType & 0xff00) >> 8);
179 header[7] = (byte)((ushort)PacketType & 0x00ff);
183 header[6] = (byte)(((ushort)PacketType.PT_BLOB & 0xff00) >> 8);
184 header[7] = (byte)((ushort)PacketType.PT_BLOB & 0x00ff);
187 header[8] = (byte)((CurrentPacket & 0xff000000) >> 24);
188 header[9] = (byte)((CurrentPacket & 0x00ff0000) >> 16);
189 header[10] = (byte)((CurrentPacket & 0x0000ff00) >> 8);
190 header[11] = (byte)(CurrentPacket & 0x000000ff);
192 header[12] = (byte)((NumberOfPackets & 0xff000000) >> 24);
193 header[13] = (byte)((NumberOfPackets & 0x00ff0000) >> 16);
194 header[14] = (byte)((NumberOfPackets & 0x0000ff00) >> 8);
195 header[15] = (byte)(NumberOfPackets & 0x000000ff);
197 header[16] = (byte)((PayloadSize & 0xff00) >> 8);
198 header[17] = (byte)(PayloadSize & 0x00ff);
200 header[18] = (byte)((uniqueToken & 0xff000000) >> 24);
201 header[19] = (byte)((uniqueToken & 0x00ff0000) >> 16);
202 header[20] = (byte)((uniqueToken & 0x0000ff00) >> 8);
203 header[21] = (byte)(uniqueToken & 0x000000ff);
209 private bool Send(PacketType PacketType, byte[] Payload)
214 bool successfull = true;
215 int packetCount = (Payload.Length / MAX_PAYLOAD_SIZE) + 1;
218 int bytesLeft = Payload.Length;
220 for (int Package = 1; Package <= packetCount; Package++)
223 if (bytesLeft > MAX_PAYLOAD_SIZE)
225 bytesToSend = MAX_PAYLOAD_SIZE;
226 bytesLeft -= bytesToSend;
230 bytesToSend = bytesLeft;
234 byte[] header = Header(PacketType, packetCount, Package, bytesToSend);
235 byte[] packet = new byte[MAX_PACKET_SIZE];
237 Array.Copy(header, 0, packet, 0, header.Length);
238 Array.Copy(Payload, bytesSent, packet, header.Length, bytesToSend);
240 int sendSize = socket.Send(packet, header.Length + bytesToSend, SocketFlags.None);
242 if (sendSize != (header.Length + bytesToSend))
248 bytesSent += bytesToSend;
264 /************************************************************************/
265 /* SendHelo - Payload format */
266 /* %s - device name (max 128 chars) */
267 /* %c - icontype ( 0=>NOICON, 1=>JPEG , 2=>PNG , 3=>GIF ) */
268 /* %s - my port ( 0=>not listening ) */
269 /* %d - reserved1 ( 0 ) */
270 /* %d - reserved2 ( 0 ) */
271 /* XX - imagedata ( can span multiple packets ) */
272 /************************************************************************/
273 public bool SendHelo(string DevName, IconType IconType, string IconFile)
276 byte[] icon = new byte[0];
277 if (IconType != IconType.ICON_NONE)
278 icon = File.ReadAllBytes(IconFile);
280 byte[] payload = new byte[DevName.Length + 12 + icon.Length];
284 for (int i = 0; i < DevName.Length; i++)
285 payload[offset++] = (byte)DevName[i];
286 payload[offset++] = (byte)'\0';
288 payload[offset++] = (byte)IconType;
290 payload[offset++] = (byte)0;
291 payload[offset++] = (byte)'\0';
293 for (int i = 0; i < 8; i++)
294 payload[offset++] = (byte)0;
296 Array.Copy(icon, 0, payload, DevName.Length + 12, icon.Length);
298 return Send(PacketType.PT_HELO, payload);
302 public bool SendHelo(string DevName)
304 return SendHelo(DevName, IconType.ICON_NONE, "");
307 /************************************************************************/
308 /* SendNotification - Payload format */
311 /* %c - icontype ( 0=>NOICON, 1=>JPEG , 2=>PNG , 3=>GIF ) */
312 /* %d - reserved ( 0 ) */
313 /* XX - imagedata ( can span multiple packets ) */
314 /************************************************************************/
315 public bool SendNotification(string Caption, string Message, IconType IconType, string IconFile)
318 byte[] icon = new byte[0];
319 if (IconType != IconType.ICON_NONE)
320 icon = File.ReadAllBytes(IconFile);
322 byte[] payload = new byte[Caption.Length + Message.Length + 7 + icon.Length];
326 for (int i = 0; i < Caption.Length; i++)
327 payload[offset++] = (byte)Caption[i];
328 payload[offset++] = (byte)'\0';
330 for (int i = 0; i < Message.Length; i++)
331 payload[offset++] = (byte)Message[i];
332 payload[offset++] = (byte)'\0';
334 payload[offset++] = (byte)IconType;
336 for (int i = 0; i < 4; i++)
337 payload[offset++] = (byte)0;
339 Array.Copy(icon, 0, payload, Caption.Length + Message.Length + 7, icon.Length);
341 return Send(PacketType.PT_NOTIFICATION, payload);
345 public bool SendNotification(string Caption, string Message)
347 return SendNotification(Caption, Message, IconType.ICON_NONE, "");
350 /************************************************************************/
351 /* SendButton - Payload format */
352 /* %i - button code */
353 /* %i - flags 0x01 => use button map/name instead of code */
354 /* 0x02 => btn down */
356 /* 0x08 => use amount */
357 /* 0x10 => queue event */
358 /* 0x20 => do not repeat */
359 /* 0x40 => virtual key */
360 /* 0x80 => axis key */
361 /* %i - amount ( 0 => 65k maps to -1 => 1 ) */
362 /* %s - device map (case sensitive and required if flags & 0x01) */
363 /* "KB" - Standard keyboard map */
364 /* "XG" - Xbox Gamepad */
365 /* "R1" - Xbox Remote */
366 /* "R2" - Xbox Universal Remote */
367 /* "LI:devicename" - valid LIRC device map where 'devicename' */
368 /* is the actual name of the LIRC device */
369 /* "JS<num>:joyname" - valid Joystick device map where */
370 /* 'joyname' is the name specified in */
371 /* the keymap. JS only supports button code */
372 /* and not button name currently (!0x01). */
373 /* %s - button name (required if flags & 0x01) */
374 /************************************************************************/
375 private bool SendButton(string Button, ushort ButtonCode, string DeviceMap, ButtonFlagsType Flags, short Amount)
378 if (Button.Length != 0)
380 if ((Flags & ButtonFlagsType.BTN_USE_NAME) == 0)
381 Flags |= ButtonFlagsType.BTN_USE_NAME;
389 if ((Flags & ButtonFlagsType.BTN_USE_AMOUNT) == 0)
390 Flags |= ButtonFlagsType.BTN_USE_AMOUNT;
393 if ((Flags & ButtonFlagsType.BTN_DOWN) == 0 && (Flags & ButtonFlagsType.BTN_UP) == 0)
394 Flags |= ButtonFlagsType.BTN_DOWN;
396 byte[] payload = new byte[Button.Length + DeviceMap.Length + 8];
400 payload[offset++] = (byte)((ButtonCode & 0xff00) >> 8);
401 payload[offset++] = (byte)(ButtonCode & 0x00ff);
403 payload[offset++] = (byte)(((ushort)Flags & 0xff00) >> 8);
404 payload[offset++] = (byte)((ushort)Flags & 0x00ff);
406 payload[offset++] = (byte)((Amount & 0xff00) >> 8);
407 payload[offset++] = (byte)(Amount & 0x00ff);
409 for (int i = 0; i < DeviceMap.Length; i++)
410 payload[offset++] = (byte)DeviceMap[i];
411 payload[offset++] = (byte)'\0';
413 for (int i = 0; i < Button.Length; i++)
414 payload[offset++] = (byte)Button[i];
415 payload[offset++] = (byte)'\0';
417 return Send(PacketType.PT_BUTTON, payload);
421 public bool SendButton(string Button, string DeviceMap, ButtonFlagsType Flags, short Amount)
423 return SendButton(Button, 0, DeviceMap, Flags, Amount);
426 public bool SendButton(string Button, string DeviceMap, ButtonFlagsType Flags)
428 return SendButton(Button, 0, DeviceMap, Flags, 0);
431 public bool SendButton(ushort ButtonCode, string DeviceMap, ButtonFlagsType Flags, short Amount)
433 return SendButton("", ButtonCode, DeviceMap, Flags, Amount);
436 public bool SendButton(ushort ButtonCode, string DeviceMap, ButtonFlagsType Flags)
438 return SendButton("", ButtonCode, DeviceMap, Flags, 0);
441 public bool SendButton(ushort ButtonCode, ButtonFlagsType Flags, short Amount)
443 return SendButton("", ButtonCode, "", Flags, Amount);
446 public bool SendButton(ushort ButtonCode, ButtonFlagsType Flags)
448 return SendButton("", ButtonCode, "", Flags, 0);
451 public bool SendButton()
453 return SendButton("", 0, "", ButtonFlagsType.BTN_UP, 0);
456 /************************************************************************/
457 /* SendPing - No payload */
458 /************************************************************************/
459 public bool SendPing()
461 byte[] payload = new byte[0];
462 return Send(PacketType.PT_PING, payload);
465 /************************************************************************/
466 /* SendBye - No payload */
467 /************************************************************************/
468 public bool SendBye()
470 byte[] payload = new byte[0];
471 return Send(PacketType.PT_BYE, payload);
474 /************************************************************************/
475 /* SendMouse - Payload format */
477 /* - 0x01 absolute position */
478 /* %i - mousex (0-65535 => maps to screen width) */
479 /* %i - mousey (0-65535 => maps to screen height) */
480 /************************************************************************/
481 public bool SendMouse(ushort X, ushort Y, MouseFlagsType Flags)
484 byte[] payload = new byte[9];
488 payload[offset++] = (byte)Flags;
490 payload[offset++] = (byte)((X & 0xff00) >> 8);
491 payload[offset++] = (byte)(X & 0x00ff);
493 payload[offset++] = (byte)((Y & 0xff00) >> 8);
494 payload[offset++] = (byte)(Y & 0x00ff);
496 return Send(PacketType.PT_MOUSE, payload);
500 public bool SendMouse(ushort X, ushort Y)
502 return SendMouse(X, Y, MouseFlagsType.MS_ABSOLUTE);
505 /************************************************************************/
506 /* SendLog - Payload format */
509 /************************************************************************/
510 public bool SendLog(LogTypeEnum LogLevel, string Message)
513 byte[] payload = new byte[Message.Length + 2];
517 payload[offset++] = (byte)LogLevel;
519 for (int i = 0; i < Message.Length; i++)
520 payload[offset++] = (byte)Message[i];
521 payload[offset++] = (byte)'\0';
523 return Send(PacketType.PT_LOG, payload);
527 /************************************************************************/
528 /* SendAction - Payload format */
529 /* %c - action type */
530 /* %s - action message */
531 /************************************************************************/
532 public bool SendAction(ActionType Action, string Message)
535 byte[] payload = new byte[Message.Length + 2];
539 payload[offset++] = (byte)Action;
541 for (int i = 0; i < Message.Length; i++)
542 payload[offset++] = (byte)Message[i];
543 payload[offset++] = (byte)'\0';
545 return Send(PacketType.PT_ACTION, payload);
549 public bool SendAction(string Message)
551 return SendAction(ActionType.ACTION_EXECBUILTIN, Message);