Skip to content
Browse files

Initial commit

  • Loading branch information...
1 parent 115efbe commit 48fbf06c45d845f0e72a22150f3972597c122a25 @DisplayCast committed
Showing with 36,770 additions and 0 deletions.
  1. +1 −0 .gitattributes
  2. +65 −0 Control Service API.txt
  3. +716 −0 ControllerService/APIresponder.cs
  4. +163 −0 ControllerService/ControllerService.csproj
  5. +61 −0 ControllerService/JSONresponses.cs
  6. +29 −0 ControllerService/Program.cs
  7. +60 −0 ControllerService/ProjectInstaller.Designer.cs
  8. +24 −0 ControllerService/ProjectInstaller.cs
  9. +129 −0 ControllerService/ProjectInstaller.resx
  10. +40 −0 ControllerService/Properties/AssemblyInfo.cs
  11. +36 −0 ControllerService/Service.Designer.cs
  12. +79 −0 ControllerService/Service.cs
  13. +3 −0 ControllerService/app.config
  14. BIN ControllerService/dc.ico
  15. +376 −0 ControllerService/monitorPlayers.cs
  16. +1,044 −0 ControllerServiceInstaller/ControllerServiceInstaller.vdproj
  17. +219 −0 DisplayCast.sln
  18. +1,482 −0 DisplayCastInstaller/DisplayCastInstaller.vdproj
  19. BIN Installers/Controller Service/ControllerServiceInstaller.msi
  20. BIN Installers/Controller Service/setup.exe
  21. BIN Installers/DisplayCast/DisplayCastInstaller.msi
  22. BIN Installers/DisplayCast/setup.exe
  23. +20 −0 Location/Location.sln
  24. +304 −0 Location/Location/ImageParser.cs
  25. +83 −0 Location/Location/Location.cs
  26. +64 −0 Location/Location/Location.csproj
  27. +25,066 −0 Location/Location/LocationService.cs
  28. +40 −0 Location/Location/Properties/AssemblyInfo.cs
  29. +162 −0 Location/Location/QueryMSE.cs
  30. +58 −0 Location/Location/XmlNoNamespaceWriter.cs
  31. +1,855 −0 Location/Location/aaaService.cs
  32. +176 −0 Player/AboutBox.Designer.cs
  33. +103 −0 Player/AboutBox.cs
  34. +926 −0 Player/AboutBox.resx
  35. +64 −0 Player/AssemblyInfo.cs
  36. +244 −0 Player/Player.csproj
  37. BIN Player/Player.ico
  38. BIN Player/Player_TemporaryKey.pfx
  39. +74 −0 Player/Properties/Resources.Designer.cs
  40. +124 −0 Player/Properties/Resources.resx
  41. +47 −0 Player/Properties/app.manifest
  42. BIN Player/Resources/displayCast-PLAY-512.png
  43. +85 −0 Player/Streamer.Designer.cs
  44. +463 −0 Player/Streamer.cs
  45. +500 −0 Player/Streamer.resx
  46. +1,170 −0 Player/StreamerList.cs
  47. +120 −0 Player/StreamerList.resx
  48. +3 −0 Player/app.config
  49. +65 −0 Shared/DisplayCastGlobals.cs
  50. +320 −0 Shared/License.rtf
  51. +40 −0 Shared/Properties/AssemblyInfo.cs
  52. +67 −0 Shared/Shared.csproj
  53. BIN Shared/dc.ico
  54. BIN Shared/displayCast-BASIC-LOGO.jpg
Sorry, we could not display the entire diff because it was too big.
View
1 .gitattributes
@@ -0,0 +1 @@
+
View
65 Control Service API.txt
@@ -0,0 +1,65 @@
+ DisplayCast Controller API
+ --------------------------
+
+The API uses an HTTP/REST interface with responses in JSONP. By default, the service runs on port 11223.
+
+e.g.,/ http://displaycast.fxpal.net:11223/connect?source=34546&sink=34256&callback=foobar
+
+STATUS:
+-------
+-whoami
+ Parameters: none
+ Returns: Returns the Player, Archiver and Streamer that is running on the same IP address as the requester. Web based client can use this call to identify all the local services.
+
+-listPlayers // sink
+-listArchivers // sink
+-listStreamers // source
+ Parameters: none
+ Returns: array of (id /* immutable and used for further operations */,
+ description /* name used by users */,
+ x, y, width,height /* dimension */,
+ locationID /* to be sent to a location service for resolution */
+ os, machineName /* ??? */
+
+- status
+ Parameters: id /* source or sink */
+ Returns: (id, description, x, y, width, height, locationID, os, machineName)
+
+- sessionStatus
+ Parameters: id /* session */
+ Returns: (id, srcID, sinkId, x, y, w, h, iconified, fullscreen)
+
+- snapshot
+ Parameters: id=id&width=num /* source */
+ Returns: PNG of desktop, optionally scaled to fit into width pixels
+
+SINK OPERATIONS:
+----------------
+- connect
+ Parameters: source=id&sink=id
+ Returns: id /* sessionID */
+
+- disconnect
+ Parameters: id /* sessionID */
+ Returns: none
+
+- move
+ Parameters: sessionID=id&x=&y=&width=&height= /* new dimensions - used for resizing and moving */
+ Returns: none /* issue a sessionstatus to get the final location */
+
+- iconify
+ Parameters: id /* sessionID, toggles state */
+ Returns: none
+
+- fullscreen
+ Parameters: id /* sessionID, toggles state */
+ Returns: none
+
+SOURCE OPERATION:
+-----------------
+- createRegion
+ Parameters: source=id&x=&y=&w=&h=
+ Returns: id /* new source id */
+
+
+
View
716 ControllerService/APIresponder.cs
@@ -0,0 +1,716 @@
+// Copyright (c) 2012, Fuji Xerox Co., Ltd.
+// All rights reserved.
+// Author: Surendar Chandra, FX Palo Alto Laboratory, Inc.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Collections;
+using System.Threading;
+using System.Web.Script.Serialization;
+using System.Diagnostics;
+using System.Drawing.Imaging;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+
+using Shared;
+
+using ZeroconfService;
+
+// Functions that JSONP responses for the requests made to the API as a HTTP/REST call
+namespace FXPAL.DisplayCast.ControllerService {
+ class APIresponder {
+ public HttpListener listener = new HttpListener();
+
+ // Keep track of active sessions (reported by the player), players/streamers/archives as well as the sources and sink NetService
+ private ArrayList sessions, players, streamers, archivers, sinkServices, sourceServices;
+ private JavaScriptSerializer serializer = new JavaScriptSerializer();
+
+ #region Utility functions to pass along commands to the actual entities (Player/Streamer)
+ /// <summary>
+ /// Utility function to send the command in 'cmd' to the bonjour 'service'.
+ /// This variant will use the addressses and port information that is advertised in 'service'.
+ /// </summary>
+ /// <param name="service">The Bonjour service that is expecting this command</param>
+ /// <param name="cmd">A telnet like command interface</param>
+ /// <returns>Returns the error message returned from this 'service' request</returns>
+ private String sendCommand(NetService service, String cmd) {
+ return sendCommand(service, -1, cmd);
+ }
+
+ /// <summary>
+ /// Utility function to send the command to the network address specified by service but to the port specified by the parameter.
+ /// Used to send 'MASK' command to Streamer (which sends the actual data in the port specified by 'service')
+ /// </summary>
+ /// <param name="service"></param>
+ /// <param name="port"></param>
+ /// <param name="cmd"></param>
+ /// <returns></returns>
+ private String sendCommand(NetService service, Int32 port, String cmd) {
+ // Bonjour hosts can be multihomed. Try all advertised addresses to see which one is appropriate for us
+ IList addresses = service.Addresses;
+
+ foreach (System.Net.IPEndPoint addr in addresses) {
+ System.Net.Sockets.TcpClient clntSocket = new System.Net.Sockets.TcpClient();
+
+ if (port != -1)
+ addr.Port = port;
+
+ try {
+ clntSocket.Connect(addr);
+ if (clntSocket.Connected) {
+ Trace.WriteLine("DEBUG: Connected to " + service.Name);
+
+ try {
+ NetworkStream strm = clntSocket.GetStream();
+ byte[] bytes = Encoding.ASCII.GetBytes(cmd);
+ strm.Write(bytes, 0, bytes.Length);
+
+ bytes = new byte[1024];
+ int readLength = strm.Read(bytes, 0, bytes.Length);
+ return System.Text.Encoding.ASCII.GetString(bytes, 0, readLength);
+ } catch {
+ return DisplayCastGlobals.CONTROL_REMOTE_FAILED + service.Name;
+ }
+ }
+ } catch (IOException) {
+ //Ignore and try the next address
+ }
+ }
+ return DisplayCastGlobals.CONTROL_REMOTE_IP_NOTFOUND + service.Name;
+ }
+
+ /// <summary>
+ /// Utility function to wrap the error message into a JSON message.
+ /// </summary>
+ /// <param name="err"></param>
+ /// <returns></returns>
+ private String JSONError(String err) {
+ JSONstatus status = new JSONstatus();
+ status.result = err;
+
+ return this.serializer.Serialize(status);
+ }
+ #endregion
+
+ /// <summary>
+ /// Callback for ControlAPI HTTP/REST requests
+ /// </summary>
+ /// <param name="result"></param>
+ protected void WebRequestCallback(IAsyncResult result) {
+ // Sanity check though this can't possibly be true
+ if (this.listener == null)
+ return;
+
+ // Remove this request and schedule for receiving future requests
+ HttpListenerContext context = this.listener.EndGetContext(result);
+ this.listener.BeginGetContext(new AsyncCallback(WebRequestCallback), this.listener);
+
+ // Process current request
+ this.ProcessRequest(context);
+ }
+
+ /// <summary>
+ /// Process the current HTTP/REST request
+ /// </summary>
+ /// <param name="Context">returned from the asynchronous httplistener</param>
+ protected virtual void ProcessRequest(HttpListenerContext Context) {
+ HttpListenerRequest request = Context.Request;
+ HttpListenerResponse response = Context.Response;
+ String cmd = request.Url.LocalPath.Substring(1).ToUpper();
+ String responseString = null;
+
+ response.StatusCode = (int)HttpStatusCode.OK;
+
+ // Giant command processing switch
+ switch (cmd) {
+ case "WHOAMI":
+ IPAddress who = request.RemoteEndPoint.Address;
+ JSONwhoami ami = new JSONwhoami();
+
+ Trace.WriteLine("DEBUG: I am " + who.ToString() + " " + request.Headers);
+ foreach (NetService service in sinkServices) {
+ IList addresses = service.Addresses;
+
+ foreach (IPEndPoint addr in addresses) {
+ Trace.WriteLine("DEBUG: am I sink " + addr.Address.ToString());
+ if (who.Equals(addr.Address)) {
+ if (service.Type.StartsWith(Shared.DisplayCastGlobals.PLAYER))
+ ami.player = service.Name;
+ if (service.Type.StartsWith(Shared.DisplayCastGlobals.ARCHIVER))
+ ami.archiver = service.Name;
+ }
+ }
+ }
+
+ foreach (NetService service in sourceServices) {
+ IList addresses = service.Addresses;
+
+ foreach (IPEndPoint addr in addresses) {
+ Trace.WriteLine("DEBUG: am I source " + addr.Address.ToString());
+ if (who.Equals(addr.Address)) {
+ if (service.Type.StartsWith(Shared.DisplayCastGlobals.STREAMER))
+ ami.streamer = service.Name;
+ }
+ }
+ }
+ responseString = serializer.Serialize(ami);
+ break;
+
+ case "LISTPLAYERS":
+ responseString = serializer.Serialize(players);
+ break;
+
+ case "LISTARCHIVERS":
+ responseString = serializer.Serialize(archivers);
+ break;
+
+ case "LISTSTREAMERS":
+ responseString = serializer.Serialize(streamers);
+ break;
+
+ case "LISTSESSIONS":
+ responseString = serializer.Serialize(sessions);
+ break;
+
+ case "STATUS":
+ if ((request.Url.Query == null) || (request.Url.Query.Length < 2)) {
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_STATUS;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ } else {
+ String id = request.Url.Query.Substring(1);
+ JSONSrcSink res = null;
+
+ // Could be status of players/archivers/streamers
+ foreach (JSONSrcSink clnt in players)
+ if (clnt.id.Equals(id)) {
+ res = clnt;
+ break;
+ }
+ if (res == null)
+ foreach (JSONSrcSink clnt in archivers)
+ if (clnt.id.Equals(id)) {
+ res = clnt;
+ break;
+ }
+ if (res == null)
+ foreach (JSONSrcSink clnt in streamers)
+ if (clnt.id.Equals(id)) {
+ res = clnt;
+ break;
+ }
+ if (res == null) {
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ response.StatusDescription = "ID: " + id + " unknown";
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR);
+ } else
+ responseString = serializer.Serialize(res);
+ }
+ break;
+
+ case "SESSIONSTATUS":
+ if ((request.Url.Query == null) || (request.Url.Query.Length < 2)) {
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_SESSIONSTATUS;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ } else {
+ String id = request.Url.Query.Substring(1);
+ JSONSession res = null;
+
+ foreach (JSONSession sess in sessions)
+ if (sess.id.Equals(id)) {
+ res = sess;
+ break;
+ }
+ if (res == null) {
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ response.StatusDescription = "ID: " + id + " unknown";
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR);
+ } else
+ responseString = serializer.Serialize(res);
+ }
+ break;
+
+ case "SNAPSHOT":
+ if ((request.Url.Query == null) || (request.Url.Query.Length < 2)) {
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_SNAPSHOT;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ } else {
+ String id = request.Url.Query.Substring(1);
+ char[] delimiters = { '=', '&' };
+ String[] words = id.Split(delimiters);
+
+ if ((words.Length < 2) || (!words[0].ToUpper().Equals("ID"))) {
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_SNAPSHOT;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ } else {
+ NetService service = null;
+
+ foreach (NetService s in sourceServices)
+ if (s.Name.Equals(words[1])) {
+ service = s;
+
+ break;
+ }
+#if DEBUG
+ else
+ responseString = responseString + "Are we: " + s.Name + "\n";
+#endif
+
+ if (service == null) {
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ response.StatusDescription = "ID: " + words[1] + " unknown";
+ responseString = JSONError(
+#if DEBUG
+ responseString +
+#endif
+ DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR + words[1]);
+ } else {
+ String port = null;
+
+ if (service.TXTRecordData != null) {
+ byte[] txt = service.TXTRecordData;
+ IDictionary dict = NetService.DictionaryFromTXTRecordData(txt);
+
+ if (dict != null) {
+ foreach (DictionaryEntry kvp in dict) {
+ String key = (String)kvp.Key;
+
+ key = key.ToUpper();
+ if (key.Equals("IMAGEPORT")) {
+ byte[] value = (byte[])kvp.Value;
+
+ try {
+ port = Encoding.UTF8.GetString(value);
+ } catch {
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (port == null) {
+ response.ContentType = "image/png";
+ Int32 reqWidth = 800, reqHeight = 600;
+
+ if ((words.Length >= 4) && (words[3].ToUpper().Equals("WIDTH"))) {
+ reqWidth = Convert.ToInt32(words[4]);
+ reqHeight = reqWidth * reqHeight / 800;
+ }
+
+ Bitmap bmp = new Bitmap(reqWidth, reqHeight, PixelFormat.Format24bppRgb);
+ Graphics g = Graphics.FromImage(bmp);
+ Font rectangleFont = new Font("Arial", 10, FontStyle.Bold);
+
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ g.Clear(Color.LightGray);
+
+ g.DrawString("Desktop snapshot from " + service.Name + " should go here", rectangleFont, SystemBrushes.WindowText, new PointF(10, 40));
+ bmp.Save(response.OutputStream, ImageFormat.Png);
+
+ g.Dispose();
+ bmp.Dispose();
+ } else {
+ IList addresses = service.Addresses;
+ int portNum = Convert.ToInt32(port);
+
+ foreach (System.Net.IPEndPoint addr in addresses) {
+ System.Net.Sockets.TcpClient clntSocket = new System.Net.Sockets.TcpClient();
+ try {
+ clntSocket.Connect(addr.Address, portNum);
+ if (clntSocket.Connected) {
+ clntSocket.Close();
+
+ response.Redirect("http://" + addr.Address.ToString() + ":" + port + request.Url.Query);
+ break;
+ }
+ } catch (SocketException) {
+ //Ignore and try the next address
+ }
+ }
+ if (response.StatusCode != (int)HttpStatusCode.Redirect) {
+ response.ContentType = "image/png";
+
+ Int32 reqWidth = 800, reqHeight = 600;
+
+ if ((words.Length >= 4) && (words[2].ToUpper().Equals("WIDTH"))) {
+ reqWidth = Convert.ToInt32(words[3]);
+ reqHeight = reqWidth * reqHeight / 800;
+ Trace.WriteLine("DEBUG: Changed image dimensions to: " + reqWidth + "x" + reqHeight);
+ }
+
+ Bitmap bmp = new Bitmap(reqWidth, reqHeight, PixelFormat.Format24bppRgb);
+ Graphics g = Graphics.FromImage(bmp);
+ Font rectangleFont = new Font("Arial", 10, FontStyle.Bold);
+
+ g.SmoothingMode = SmoothingMode.AntiAlias;
+ g.Clear(Color.LightGray);
+
+ g.DrawString("Desktop " + service.Name + "at " + port + " not responding", rectangleFont, SystemBrushes.WindowText, new PointF(10, 40));
+ bmp.Save(response.OutputStream, ImageFormat.Png);
+
+ g.Dispose();
+ bmp.Dispose();
+ }
+ }
+
+ response.Close();
+ return;
+ }
+ }
+ }
+ break;
+
+ case "CONNECT":
+ if ((request.Url.Query == null) || (request.Url.Query.Length < 2)) {
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_CONNECT;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ } else {
+ String q = request.Url.Query.Substring(1);
+ char[] delimiters = { '=', '&' };
+ String[] words = q.Split(delimiters);
+
+ if (((words.Length == 4) || (words.Length == 6)) && words[0].ToUpper().Equals("SOURCE") && words[2].ToUpper().Equals("SINK")) {
+ String srcId = words[1], sinkId = words[3];
+ NetService service = null;
+
+ foreach (NetService s in sinkServices)
+ if (s.Name.Equals(sinkId)) {
+ service = s;
+ break;
+ }
+
+ // We don't sanity check sources. Let the sink report an error, maybe the sink knows something that we don't know
+ if (service == null) {
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ response.StatusDescription = "Unknown sink: " + sinkId;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR);
+ } else {
+ JSONnewSession ns = new JSONnewSession();
+ ns.id = sendCommand(service, "SHOW " + srcId + "\n");
+ responseString = serializer.Serialize(ns);
+ }
+ } else {
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_CONNECT;
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ }
+ }
+ break;
+
+ case "DISCONNECT":
+ if ((request.Url.Query == null) || (request.Url.Query.Length < 2)) {
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_DISCONNECT;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ } else {
+ String g = request.Url.Query.Substring(1);
+ char[] delimiters = { '=', '&' };
+ String[] words = g.Split(delimiters);
+ NetService service = null;
+ String sinkId = null;
+
+ if (((words.Length == 4) || (words.Length == 2)) & (words[0].ToUpper().Equals("ID"))) {
+ String debug = null;
+ foreach (JSONSession sess in sessions) {
+ debug = debug + " " + sess.id;
+ if (words[1].Equals(sess.id)) {
+ sinkId = sess.sinkId;
+
+ break;
+ }
+ }
+ if (sinkId == null) {
+ response.StatusDescription = "Unknown session ID: " + words[1];
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ responseString = JSONError("UNKNOWN ID: " + words[1] + " looked at " + debug);
+ } else {
+ foreach (NetService s in sinkServices)
+ if (s.Name.Equals(sinkId)) {
+ service = s;
+ break;
+ }
+
+ if (service == null) {
+ response.StatusDescription = "Unknown sink ID: " + sinkId;
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR);
+ } else {
+ JSONstatus status = new JSONstatus();
+ status.result = sendCommand(service, "CLOSE " + words[1] + "\n");
+ responseString = serializer.Serialize(status);
+ }
+ }
+ } else {
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_DISCONNECT;
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ }
+
+ }
+ break;
+
+ case "MOVE":
+ if ((request.Url.Query == null) || (request.Url.Query.Length < 2)) {
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_MOVE;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ } else {
+ String q = request.Url.Query.Substring(1);
+ char[] delimiters = { '=', '&' };
+ String[] words = q.Split(delimiters);
+
+ if (((words.Length == 10) || (words.Length == 12)) && words[0].ToUpper().Equals("SESSIONID") && words[2].ToUpper().Equals("X") && words[4].ToUpper().Equals("Y") && words[6].ToUpper().Equals("WIDTH") && words[8].ToUpper().Equals("HEIGHT")) {
+ NetService service = null;
+ String sinkId = null;
+ JSONSession session = null;
+ String sessId = words[1];
+
+ foreach (JSONSession sess in sessions)
+ if (sessId.Equals(sess.id)) {
+ sinkId = sess.sinkId;
+ session = sess;
+ break;
+ }
+
+ if (sinkId == null) {
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ response.StatusDescription = "Unknown session: " + sessId;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR);
+ } else {
+ foreach (NetService s in sinkServices)
+ if (s.Name.Equals(sinkId)) {
+ service = s;
+ break;
+ }
+
+ if (service == null) {
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ response.StatusDescription = "Unknown sink";
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR);
+ } else {
+ JSONstatus status = new JSONstatus();
+ status.result = sendCommand(service, "MOVE " + sessId + " " + words[3] + "x" + words[5] + "x" + words[7] + "x" + words[9] + "\n");
+
+ responseString = serializer.Serialize(status);
+ }
+ }
+ } else {
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_MOVE;
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ }
+ }
+ break;
+
+ case "ICONIFY":
+ if ((request.Url.Query == null) || (request.Url.Query.Length < 2)) {
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_ICON;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ } else {
+ String sessId = request.Url.Query.Substring(1);
+ NetService service = null;
+ String sinkId = null;
+ JSONSession session = null;
+
+ foreach (JSONSession sess in sessions)
+ if (sessId.Equals(sess.id)) {
+ sinkId = sess.id;
+ session = sess;
+ break;
+ }
+
+ if (sinkId == null) {
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ response.StatusDescription = "Unknown session: " + sessId;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR);
+ } else {
+ foreach (NetService s in sinkServices)
+ if (s.Name.Equals(sinkId)) {
+ service = s;
+ break;
+ }
+
+ if (service == null) {
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ response.StatusDescription = "Unknown sink";
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR);
+ } else {
+ JSONstatus status = new JSONstatus();
+ status.result = sendCommand(service, ((session.iconified == 0) ? "ICON " : "DICO ") + sessId + "\n");
+ responseString = serializer.Serialize(status);
+ }
+
+ }
+ }
+ break;
+
+ case "FULLSCREEN":
+ if ((request.Url.Query == null) || (request.Url.Query.Length < 2)) {
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_FULLSCREEN;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ } else {
+ String sessId = request.Url.Query.Substring(1);
+ NetService service = null;
+ String sinkId = null;
+ JSONSession session = null;
+
+ foreach (JSONSession sess in sessions)
+ if (sessId.Equals(sess.id)) {
+ sinkId = sess.id;
+ session = sess;
+ break;
+ }
+
+ if (sinkId == null) {
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ response.StatusDescription = "Unknown id: " + sinkId;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR);
+ } else {
+ foreach (NetService s in sinkServices)
+ if (s.Name.Equals(sinkId)) {
+ service = s;
+ break;
+ }
+
+ if (service == null) {
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ response.StatusDescription = "Unknown sink";
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR);
+ } else {
+ JSONstatus status = new JSONstatus();
+ status.result = sendCommand(service, ((session.fullScreen == 0) ? "FS " : "SF ") + sessId + "\n");
+ responseString = serializer.Serialize(status);
+ }
+ }
+ }
+ break;
+
+ case "MASK":
+ case "CREATEREGION":
+ if ((request.Url.Query == null) || (request.Url.Query.Length < 2)) {
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_MASK;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ } else {
+ String q = request.Url.Query.Substring(1);
+ char[] delimiters = { '=', '&' };
+ String[] words = q.Split(delimiters);
+
+ if (((words.Length == 10) || (words.Length == 12)) && words[0].ToUpper().Equals("SOURCE") && words[2].ToUpper().Equals("X") && words[4].ToUpper().Equals("Y") && words[6].ToUpper().Equals("WIDTH") && words[8].ToUpper().Equals("HEIGHT")) {
+ NetService service = null;
+ String srcId = words[1];
+
+ foreach (NetService s in sourceServices)
+ if (srcId.Equals(s.Name)) {
+ service = s;
+ break;
+ }
+
+ if (service == null) {
+ response.StatusCode = (int)HttpStatusCode.NotFound;
+ response.StatusDescription = "Unknown source: " + srcId;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR);
+ } else {
+ Int32 port = -1;
+
+ if (service.TXTRecordData != null) {
+ byte[] txt = service.TXTRecordData;
+ IDictionary dict = NetService.DictionaryFromTXTRecordData(txt);
+
+ if (dict != null) {
+ foreach (DictionaryEntry kvp in dict) {
+ String key = (String)kvp.Key;
+
+ key = key.ToUpper();
+ if (key.Equals("MASKPORT")) {
+ byte[] value = (byte[])kvp.Value;
+
+ try {
+ port = Convert.ToInt32(Encoding.UTF8.GetString(value));
+ } catch {
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (port > 0) {
+ JSONstatus status = new JSONstatus();
+ status.result = sendCommand(service, port, "MASK " + words[3] + " " + words[5] + " " + words[7] + " " + words[9] + "\n");
+
+ responseString = serializer.Serialize(status);
+ } else {
+ response.StatusDescription = "Streamer does not support masking";
+ response.StatusCode = (int)HttpStatusCode.NotImplemented;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNIMPL_ERROR);
+ }
+ }
+ } else {
+ response.StatusDescription = DisplayCastGlobals.CONTROL_USAGE_MASK;
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_SYNTAX_ERROR);
+ }
+ }
+ break;
+
+ default:
+ response.StatusCode = (int)HttpStatusCode.BadRequest;
+ response.StatusDescription = "Unknown command: " + request.RawUrl;
+ responseString = JSONError(DisplayCastGlobals.CONTROL_JSON_UNKNOWN_ERROR + request.RawUrl + " " + cmd);
+ break;
+ }
+
+ // Now wrap in JSONP response
+ if (response.StatusCode == (int)HttpStatusCode.OK) {
+ var sb = new System.Text.StringBuilder();
+ var queryString = System.Web.HttpUtility.ParseQueryString(request.Url.Query);
+ var callback = queryString["callback"] ?? "callback";
+ sb.Append(callback + "(" + responseString + ");");
+ responseString = sb.ToString();
+ }
+
+ // Send the response back
+ byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
+ response.ContentLength64 = buffer.Length;
+ response.Headers.Set(HttpResponseHeader.Server, "FXPAL DisplayCast/" + DisplayCastGlobals.DISPLAYCAST_VERSION + " and not");
+ response.OutputStream.Write(buffer, 0, buffer.Length);
+ response.Close();
+ }
+
+ /// <summary>
+ /// Class constructor
+ /// </summary>
+ /// <param name="sessions">Array to hold sessions</param>
+ /// <param name="players">Array to hold players</param>
+ /// <param name="streamers">Array to hold streamers</param>
+ /// <param name="archivers">Array to hold all archiers</param>
+ /// <param name="sinks">NetService array of all sinks</param>
+ /// <param name="sources">NetService array of all sources</param>
+ public APIresponder(ArrayList sessions, ArrayList players, ArrayList streamers, ArrayList archivers, ArrayList sinks, ArrayList sources) {
+ this.sessions = sessions;
+ this.players = players;
+ this.streamers = streamers;
+ this.archivers = archivers;
+ this.sinkServices = sinks;
+ this.sourceServices = sources;
+
+ listener.Prefixes.Add( Shared.DisplayCastGlobals.CONTROL_API_URL);
+ listener.Start();
+
+ IAsyncResult result = this.listener.BeginGetContext(new AsyncCallback(WebRequestCallback), this.listener);
+ }
+ }
+}
View
163 ControllerService/ControllerService.csproj
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+ <ProductVersion>8.0.30703</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{DEAB3158-3B56-4E30-BC9D-D17BE2DBB219}</ProjectGuid>
+ <OutputType>WinExe</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>FXPAL.DisplayCast.ControllerService</RootNamespace>
+ <AssemblyName>FXPAL.DisplayCast.ControllerService</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <TargetFrameworkProfile>
+ </TargetFrameworkProfile>
+ <FileAlignment>512</FileAlignment>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <SccProjectName>Svn</SccProjectName>
+ <SccLocalPath>Svn</SccLocalPath>
+ <SccAuxPath>Svn</SccAuxPath>
+ <SccProvider>SubversionScc</SccProvider>
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <ApplicationRevision>0</ApplicationRevision>
+ <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <PlatformTarget>x86</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <PlatformTarget>x86</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>USE_BITMAP_COMPRESS;USE_BLUETOOTH;USE_IONIC_ZLIB_N;USE_WIFI_LOCALIZATION_N;PLAYER_TASKBAR;CONTROLLER_DEBUG_SERVICE_N</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup>
+ <StartupObject>FXPAL.DisplayCast.ControllerService.Program</StartupObject>
+ </PropertyGroup>
+ <PropertyGroup>
+ <ApplicationIcon>dc.ico</ApplicationIcon>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Configuration.Install" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Management" />
+ <Reference Include="System.Web" />
+ <Reference Include="System.Web.Extensions" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.ServiceProcess" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="APIresponder.cs" />
+ <Compile Include="JSONresponses.cs" />
+ <Compile Include="monitorPlayers.cs" />
+ <Compile Include="ProjectInstaller.cs">
+ <SubType>Component</SubType>
+ </Compile>
+ <Compile Include="ProjectInstaller.Designer.cs">
+ <DependentUpon>ProjectInstaller.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Service.cs">
+ <SubType>Component</SubType>
+ </Compile>
+ <Compile Include="Service.Designer.cs">
+ <DependentUpon>Service.cs</DependentUpon>
+ </Compile>
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="app.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="ProjectInstaller.resx">
+ <DependentUpon>ProjectInstaller.cs</DependentUpon>
+ </EmbeddedResource>
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="dc.ico">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\Shared\Shared.csproj">
+ <Project>{7C03A308-2C6B-489F-A009-AE224B92FE26}</Project>
+ <Name>Shared</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\ZeroconfService\ZeroconfService.csproj">
+ <Project>{DF49DC31-7E4D-44C2-8E35-51E61762265A}</Project>
+ <Name>ZeroconfService</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include=".NETFramework,Version=v4.0">
+ <Visible>False</Visible>
+ <ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Client.3.5">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
+ <Visible>False</Visible>
+ <ProductName>Windows Installer 3.1</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <ItemGroup>
+ <COMReference Include="Bonjour">
+ <Guid>{18FBED6D-F2B7-4EC8-A4A4-46282E635308}</Guid>
+ <VersionMajor>1</VersionMajor>
+ <VersionMinor>0</VersionMinor>
+ <Lcid>0</Lcid>
+ <WrapperTool>tlbimp</WrapperTool>
+ <Isolated>False</Isolated>
+ <EmbedInteropTypes>True</EmbedInteropTypes>
+ </COMReference>
+ </ItemGroup>
+ <ItemGroup>
+ <WCFMetadata Include="Service References\" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
View
61 ControllerService/JSONresponses.cs
@@ -0,0 +1,61 @@
+// Copyright (c) 2012, Fuji Xerox Co., Ltd.
+// All rights reserved.
+// Author: Surendar Chandra, FX Palo Alto Laboratory, Inc.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace FXPAL.DisplayCast.ControllerService {
+ // Responses sent to end users as JSON objects
+ /// <summary>
+ /// Structure for sources (Streamer) and Sinks (Players, Archivers)
+ /// </summary>
+ class JSONSrcSink {
+ public String id; // Immutable ID.
+ public String description; // User defined name. Names are used by users and can change at any time
+ public int x, y, width, height; // Dimensions in pixels
+ public int maskX, maskY, maskWidth, maskHeight; // The API now sends out both the actual screen dimensions (above) and the masked region
+ public String locationID; // Must be sent to the location server for resolution
+
+ public String os; // Just in case
+ public String machineName; // Just in case
+ public String userName; // User name for better sorting of Streamers
+ public String nearBy; // Currently uses BlueTooth to locate nearby players
+ // public Double version; // Make sure that we are talking to the right person
+ // public int imagePort;
+ }
+
+ /// <summary>
+ /// Sessions. They are reported by Players/Archivers
+ /// </summary>
+ class JSONSession {
+ public String id; // Immutable ID.
+ public String srcId; // JSONSrcSrink.id
+ public String sinkId; // JSONSrcSrink.id
+ public int x, y, width, height; // Location on sink
+ public int iconified; // window state
+ public int fullScreen; // window state
+ }
+
+ /// <summary>
+ /// One a successful session creation, return the new session ID
+ /// </summary>
+ class JSONnewSession {
+ public String id; // Immutable ID.
+ }
+
+ /// <summary>
+ /// Return the status of the operation
+ /// </summary>
+ class JSONstatus {
+ public String result;
+ }
+
+ class JSONwhoami {
+ public String player; // Return the Player running at the IP address which sends a WHOAMI command
+ public String streamer; // Returns the Stream
+ public String archiver; // Returns the Archiver
+ }
+}
View
29 ControllerService/Program.cs
@@ -0,0 +1,29 @@
+// Copyright (c) 2012, Fuji Xerox Co., Ltd.
+// All rights reserved.
+// Author: Surendar Chandra, FX Palo Alto Laboratory, Inc.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.ServiceProcess;
+using System.Text;
+
+namespace FXPAL.DisplayCast.ControllerService {
+ static class Program {
+ /// <summary>
+ /// The main entry point for the application.
+ /// </summary>
+ static void Main() {
+#if CONTROLLER_DEBUG_SERVICE
+ Service s = new Service();
+ s.Start();
+#else
+ ServiceBase[] ServicesToRun;
+ ServicesToRun = new ServiceBase[] {
+ new Service()
+ };
+ ServiceBase.Run(ServicesToRun);
+#endif
+ }
+ }
+}
View
60 ControllerService/ProjectInstaller.Designer.cs
@@ -0,0 +1,60 @@
+// Copyright (c) 2012, Fuji Xerox Co., Ltd.
+// All rights reserved.
+// Author: Surendar Chandra, FX Palo Alto Laboratory, Inc.
+
+namespace FXPAL.DisplayCast.ControllerService {
+ partial class ProjectInstaller {
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.IContainer components = null;
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+ protected override void Dispose(bool disposing) {
+ if (disposing && (components != null)) {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent() {
+ this.serviceProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller();
+ this.serviceInstaller = new System.ServiceProcess.ServiceInstaller();
+ //
+ // serviceProcessInstaller
+ //
+ this.serviceProcessInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
+ this.serviceProcessInstaller.Password = null;
+ this.serviceProcessInstaller.Username = null;
+ //
+ // serviceInstaller
+ //
+ this.serviceInstaller.Description = "FXPAL DisplayCast Controller";
+ this.serviceInstaller.DisplayName = "DisplayCast Controller";
+ this.serviceInstaller.ServiceName = "Service";
+ this.serviceInstaller.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
+ this.serviceInstaller.AfterInstall += new System.Configuration.Install.InstallEventHandler(this.serviceInstaller1_AfterInstall);
+ //
+ // ProjectInstaller
+ //
+ this.Installers.AddRange(new System.Configuration.Install.Installer[] {
+ this.serviceProcessInstaller,
+ this.serviceInstaller});
+
+ }
+
+ #endregion
+
+ private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller;
+ private System.ServiceProcess.ServiceInstaller serviceInstaller;
+ }
+}
View
24 ControllerService/ProjectInstaller.cs
@@ -0,0 +1,24 @@
+// Copyright (c) 2012, Fuji Xerox Co., Ltd.
+// All rights reserved.
+// Author: Surendar Chandra, FX Palo Alto Laboratory, Inc.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Configuration.Install;
+using System.Linq;
+
+
+namespace FXPAL.DisplayCast.ControllerService {
+ [RunInstaller(true)]
+ public partial class ProjectInstaller : System.Configuration.Install.Installer {
+ public ProjectInstaller() {
+ InitializeComponent();
+ }
+
+ private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e) {
+
+ }
+ }
+}
View
129 ControllerService/ProjectInstaller.resx
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <metadata name="serviceProcessInstaller.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <value>17, 54</value>
+ </metadata>
+ <metadata name="serviceInstaller.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <value>187, 17</value>
+ </metadata>
+ <metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+ <value>False</value>
+ </metadata>
+</root>
View
40 ControllerService/Properties/AssemblyInfo.cs
@@ -0,0 +1,40 @@
+// Copyright (c) 2012, Fuji Xerox Co., Ltd.
+// All rights reserved.
+// Author: Surendar Chandra, FX Palo Alto Laboratory, Inc.
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DisplayCastController")]
+[assembly: AssemblyDescription("FXPAL DisplayCast Controller")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("FX Palo Alto Laboratory Inc.")]
+[assembly: AssemblyProduct("ControllerService")]
+[assembly: AssemblyCopyright("Copyright ©2012, FX Palo Alto Laboratory Inc.")]
+[assembly: AssemblyTrademark("All rights reserved")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("8c55268c-a191-44fb-bea5-8112e48dc0bb")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
View
36 ControllerService/Service.Designer.cs
@@ -0,0 +1,36 @@
+// Copyright (c) 2012, Fuji Xerox Co., Ltd.
+// All rights reserved.
+// Author: Surendar Chandra, FX Palo Alto Laboratory, Inc.
+
+namespace FXPAL.DisplayCast.ControllerService {
+ partial class Service {
+ /// <summary>
+ /// Required designer variable.
+ /// </summary>
+ private System.ComponentModel.IContainer components = null;
+
+ /// <summary>
+ /// Clean up any resources being used.
+ /// </summary>
+ /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+ protected override void Dispose(bool disposing) {
+ if (disposing && (components != null)) {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Component Designer generated code
+
+ /// <summary>
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ /// </summary>
+ private void InitializeComponent() {
+ components = new System.ComponentModel.Container();
+ this.ServiceName = "Service1";
+ }
+
+ #endregion
+ }
+}
View
79 ControllerService/Service.cs
@@ -0,0 +1,79 @@
+// Copyright (c) 2012, Fuji Xerox Co., Ltd.
+// All rights reserved.
+// Author: Surendar Chandra, FX Palo Alto Laboratory, Inc.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Diagnostics;
+using System.Linq;
+using System.ServiceProcess;
+using System.Text;
+using System.Threading;
+using System.Collections;
+
+namespace FXPAL.DisplayCast.ControllerService {
+ public partial class Service : ServiceBase {
+ private ArrayList sessionList = new ArrayList();
+ private ArrayList playerList = new ArrayList();
+ private ArrayList streamerList = new ArrayList();
+ private ArrayList archiverList = new ArrayList();
+ private ArrayList sinkServicesList = new ArrayList();
+ private ArrayList sourceServicesList = new ArrayList();
+
+ private monitorPlayers players = null;
+ private APIresponder api = null;
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="args"></param>
+ protected override void OnStart(string[] args) {
+ Start();
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public void Start() {
+ players = new monitorPlayers(sessionList, playerList, streamerList, archiverList, sinkServicesList, sourceServicesList);
+ api = new APIresponder(sessionList, playerList, streamerList, archiverList, sinkServicesList, sourceServicesList);
+
+#if CONTROLLER_DEBUG_SERVICE
+ while (true) {
+ Thread.Sleep(100);
+ }
+#endif
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ protected override void OnStop() {
+ if (players != null) {
+ try {
+ players.playerBrowser.Stop();
+ players.archiveBrowser.Stop();
+ players.streamerBrowser.Stop();
+ } catch (Exception) {
+ }
+ }
+
+ if (api != null) {
+ try {
+ api.listener.Stop();
+ } catch (Exception) {
+ }
+ }
+
+ this.ExitCode = 0;
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public Service() {
+ InitializeComponent();
+ }
+ }
+}
View
3 ControllerService/app.config
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<configuration>
+<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
View
BIN ControllerService/dc.ico
Binary file not shown.
View
376 ControllerService/monitorPlayers.cs
@@ -0,0 +1,376 @@
+// Copyright (c) 2012, Fuji Xerox Co., Ltd.
+// All rights reserved.
+// Author: Surendar Chandra, FX Palo Alto Laboratory, Inc.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Drawing;
+
+using ZeroconfService;
+using Shared;
+
+using System.ComponentModel;
+using System.Threading;
+using System.Diagnostics;
+using System.Collections;
+
+namespace FXPAL.DisplayCast.ControllerService {
+ // Monitors Bonjour for the status of Streamers/Archviers/Players
+ class monitorPlayers {
+ public NetServiceBrowser playerBrowser, streamerBrowser, archiveBrowser;
+
+ private ArrayList sessions, players, streamers, archivers, sinkServices, sourceServices;
+
+ #region Utility functions
+ // Returns the appropriate Arraylist for the service
+ private ArrayList getList(String type) {
+ if (type.StartsWith(Shared.DisplayCastGlobals.PLAYER))
+ return players;
+ else if (type.StartsWith(Shared.DisplayCastGlobals.STREAMER))
+ return streamers;
+ else if (type.StartsWith(Shared.DisplayCastGlobals.ARCHIVER))
+ return archivers;
+ return null;
+ }
+
+ /// <summary>
+ /// TXT records contain additional attributes of services
+ /// </summary>
+ /// <param name="service"></param>
+ /// <param name="player"></param>
+ private void processTXTrecord(NetService service, JSONSrcSink player) {
+ byte[] txt = service.TXTRecordData;
+ IDictionary dict = NetService.DictionaryFromTXTRecordData(txt);
+
+ if (dict == null)
+ return;
+
+ // Remove all sessions from this Player so that we can add all the new entries from this TXT record
+ ArrayList itemsToRemove = new ArrayList();
+ lock (sessions.SyncRoot) {
+ foreach (JSONSession nxtSess in sessions) {
+ if (service.Name.Equals(nxtSess.sinkId))
+ itemsToRemove.Add(nxtSess);
+ };
+
+ if (itemsToRemove.Count > 0) {
+ foreach (JSONSession sess in itemsToRemove)
+ sessions.Remove(sess);
+ itemsToRemove.Clear();
+ }
+ }
+
+ foreach (DictionaryEntry kvp in dict) {
+ String key = ((String)kvp.Key).ToUpper();
+ String value = null;
+ try {
+ value = Encoding.UTF8.GetString((byte[])kvp.Value);
+ } catch {
+ // All displaycast values are strings!!
+ continue;
+ }
+
+ switch (key) {
+ case "NAME":
+ player.description = value;
+ break;
+
+ case "OSVERSION":
+ player.os = value;
+ break;
+
+ case "MACHINENAME":
+ player.machineName = value;
+ break;
+
+ case "LOCATIONID":
+ player.locationID = value;
+ break;
+
+ case "VERSION":
+ Trace.WriteLine("DEBUG: No use for version info in the API");
+ // player.version = Convert.ToDouble(value);
+ break;
+
+ case "IMAGEPORT":
+ Trace.WriteLine("DEBUG: No use for image port in the session object");
+ // player.imagePort = Convert.ToInt32(value);
+ break;
+
+ case "USERID":
+ player.userName = value;
+ break;
+
+ case "NEARBY":
+ player.nearBy = value;
+ break;
+
+ case "MASKPORT":
+ Trace.WriteLine("DEBUG: No use for Mask port info in the API");
+ break;
+
+ case "BLUETOOTH":
+ Trace.WriteLine("DEBUG: No use for Bluetooth ID's in the API");
+ break;
+
+ case "MASKSCREEN":
+ try {
+ char[] separator = { ' ', 'x' };
+ String[] words = value.Split(separator);
+
+ player.maskX = Convert.ToInt32(words[0]);
+ player.maskY = Convert.ToInt32(words[1]);
+ player.maskWidth = Convert.ToInt32(words[2]);
+ player.maskHeight = Convert.ToInt32(words[3]);
+ } catch (FormatException) {
+ player.maskX = player.maskY = player.maskWidth = player.maskHeight = 0;
+ }
+ break;
+
+ default:
+ if (key.StartsWith("SCREEN")) { // Could be screen0, screen1 etc.
+ Rectangle oldRect = new Rectangle(player.x, player.y, player.width, player.height);
+ char[] separator = { ' ', 'x' };
+ String[] words = value.Split(separator);
+
+ Rectangle newRect = new Rectangle();
+ try {
+ newRect.X = Convert.ToInt32(words[0]);
+ newRect.Y = Convert.ToInt32(words[1]);
+ newRect.Width = Convert.ToInt32(words[2]);
+ newRect.Height = Convert.ToInt32(words[3]);
+ } catch (FormatException) {
+ continue;
+ }
+
+ oldRect = Rectangle.Union(oldRect, newRect);
+ player.x = oldRect.X;
+ player.y = oldRect.Y;
+ player.width = oldRect.Width;
+ player.height = oldRect.Height;
+ } else { // Sessions
+ char[] separator = { ' ' };
+ String[] words = value.Split(separator);
+ JSONSession sess = null;
+
+ if (words.Length == 8) {
+ // This shouldn't match anymore because we remove all sessions involving this player
+ foreach (JSONSession nxtSess in sessions) {
+ if (key.Equals(nxtSess.id)) {
+ sess = nxtSess;
+
+ break;
+ }
+ };
+
+ if (sess == null) {
+ sess = new JSONSession();
+ sess.id = key;
+ sessions.Add(sess);
+ };
+ sess.srcId = words[0];
+ sess.sinkId = words[1];
+ try {
+ sess.x = Convert.ToInt32(words[2]);
+ sess.y = Convert.ToInt32(words[3]);
+ sess.width = Convert.ToInt32(words[4]);
+ sess.height = Convert.ToInt32(words[5]);
+ sess.iconified = Convert.ToInt32(words[6]);
+ sess.fullScreen = Convert.ToInt32(words[7]);
+ } catch (FormatException) {
+ // Would rather have all correct sessions than partially correct sessions
+ sessions.Remove(sess);
+ }
+
+ Trace.WriteLine("DEBUG: " + sess.id + " at " + sess.width + " x " + sess.height);
+
+ } else
+ Trace.WriteLine("FATAL: Unknown attribute " + key + ":" + value);
+ }
+ break;
+ }
+ }
+ }
+ #endregion
+
+ #region Bonjour browser callback functions
+ /// <summary>
+ /// Bonjour callback
+ /// </summary>
+ /// <param name="service"></param>
+ private void didUpdateTXT(NetService service) {
+ ArrayList list = getList(service.Type);
+
+ if (list == null)
+ return;
+
+ lock (list.SyncRoot) {
+ foreach (JSONSrcSink player in list) {
+ if (player.id.Equals(service.Name)) {
+ processTXTrecord(service, player);
+
+ // Kludge. Sometimes, we miss the TXT update record from Bonjour.
+ // So, we stop and restart a new monitoring session. That forces the system to continously get updates/stop/new update/stop, yuck
+ service.StopMonitoring();
+
+ service.DidUpdateTXT += new NetService.ServiceTXTUpdated(didUpdateTXT);
+ service.StartMonitoring();
+
+ return;
+ }
+ }
+ }
+
+ }
+
+ /// <summary>
+ /// Bonjour callback. Remember resolved services and initiate monitoring TXT record updates
+ /// </summary>
+ /// <param name="service"></param>
+ private void didResolvePlayers(NetService service) {
+ JSONSrcSink newPlayer = new JSONSrcSink();
+
+ newPlayer.id = service.Name;
+ if (service.TXTRecordData != null)
+ processTXTrecord(service, newPlayer);
+
+ // Remove any previous remembered entries
+ ArrayList list = getList(service.Type);
+ lock (list.SyncRoot) {
+ ArrayList toRemove = new ArrayList();
+
+ foreach (JSONSrcSink item in list) {
+ if (item.id.Equals(service.Name))
+ toRemove.Add(item);
+ }
+ if (toRemove.Count > 0) {
+ foreach (JSONSrcSink item in toRemove)
+ list.Remove(item);
+ toRemove.Clear();
+ }
+
+ list.Add(newPlayer);
+ }
+
+ // Store the service in either sink or sources.
+ ArrayList services = null;
+ if (service.Type.StartsWith(Shared.DisplayCastGlobals.PLAYER) || service.Type.StartsWith(Shared.DisplayCastGlobals.ARCHIVER))
+ services = sinkServices;
+
+ if (service.Type.StartsWith(Shared.DisplayCastGlobals.STREAMER))
+ services = sourceServices;
+ Debug.Assert(services != null);
+
+ // First remove any old entries
+ lock (services.SyncRoot) {
+ ArrayList toRemove = new ArrayList();
+
+ foreach (NetService item in services) {
+ if (item.Name.Equals(service.Name))
+ toRemove.Add(item);
+ }
+ if (toRemove.Count > 0) {
+ foreach (NetService item in toRemove)
+ services.Remove(item);
+ toRemove.Clear();
+ }
+
+ // Add the current service
+ services.Add(service);
+ }
+
+ service.DidUpdateTXT += new NetService.ServiceTXTUpdated(didUpdateTXT);
+ service.StartMonitoring();
+ }
+
+ /// <summary>
+ /// Bonjour callback. We don't remember services until they are resolved
+ /// </summary>
+ /// <param name="browser"></param>
+ /// <param name="service"></param>
+ /// <param name="moreComing"></param>
+ private void didFindPlayers(NetServiceBrowser browser, NetService service, bool moreComing) {
+ service.DidResolveService += new NetService.ServiceResolved(didResolvePlayers);
+ service.ResolveWithTimeout(5);
+ }
+
+ /// <summary>
+ /// Bonjour callback - cleanup removed services
+ /// </summary>
+ /// <param name="browser"></param>
+ /// <param name="service"></param>
+ /// <param name="moreComing"></param>
+ private void didRemovePlayers(NetServiceBrowser browser, NetService service, bool moreComing) {
+ // First remove from list of known players/streamers/archivers
+ ArrayList list = getList(service.Type);
+ if (list == null)
+ return;
+
+ ArrayList itemsToRemove = new ArrayList();
+ lock (list.SyncRoot) {
+ foreach (JSONSrcSink player in list) {
+ if (player.id.Equals(service.Name)) {
+ itemsToRemove.Add(player);
+ break;
+ }
+ }
+ if (itemsToRemove.Count > 0) {
+ foreach (JSONSrcSink player in itemsToRemove)
+ list.Remove(player);
+ itemsToRemove.Clear();
+ return;
+ }
+ }
+
+ // now remove the services
+ if (service.Type.StartsWith(Shared.DisplayCastGlobals.PLAYER) || service.Type.StartsWith(Shared.DisplayCastGlobals.ARCHIVER))
+ lock(sinkServices.SyncRoot)
+ sinkServices.Remove(service);
+ if (service.Type.StartsWith(Shared.DisplayCastGlobals.STREAMER))
+ lock (sourceServices.SyncRoot)
+ sourceServices.Remove(service);
+
+ service.Stop();
+ }
+ #endregion
+
+ /// <summary>
+ /// Constructor
+ /// </summary>
+ /// <param name="sessions">Array of sessions: shared with API APIresponder</param>
+ /// <param name="players">Array of players: shared with API APIresponder</param>
+ /// <param name="streamers">Array of streamers: shared with API APIresponder</param>
+ /// <param name="archivers">Array of archivers: shared with API APIresponder</param>
+ /// <param name="sinkServices">Array of netservice sinks: shared with API APIresponder</param>
+ /// <param name="sourceServices">Array of netservice sources: shared with API APIresponder</param>
+ public monitorPlayers(ArrayList sessions, ArrayList players, ArrayList streamers, ArrayList archivers, ArrayList sinkServices, ArrayList sourceServices) {
+ this.sessions = sessions;
+ this.players = players;
+ this.streamers = streamers;
+ this.archivers = archivers;
+ this.sinkServices = sinkServices;
+ this.sourceServices = sourceServices;
+
+ // Start browsing for Players/Streamers and Archivers
+ playerBrowser = new NetServiceBrowser();
+ playerBrowser.AllowMultithreadedCallbacks = true;
+ playerBrowser.DidFindService += new NetServiceBrowser.ServiceFound(didFindPlayers);
+ playerBrowser.DidRemoveService += new NetServiceBrowser.ServiceRemoved(didRemovePlayers);
+ playerBrowser.SearchForService(Shared.DisplayCastGlobals.PLAYER, Shared.DisplayCastGlobals.BONJOURDOMAIN);
+
+ streamerBrowser = new NetServiceBrowser();
+ streamerBrowser.AllowMultithreadedCallbacks = true;
+ streamerBrowser.DidFindService += new NetServiceBrowser.ServiceFound(didFindPlayers);
+ streamerBrowser.DidRemoveService += new NetServiceBrowser.ServiceRemoved(didRemovePlayers);
+ streamerBrowser.SearchForService(Shared.DisplayCastGlobals.STREAMER, Shared.DisplayCastGlobals.BONJOURDOMAIN);
+
+ archiveBrowser = new NetServiceBrowser();
+ archiveBrowser.AllowMultithreadedCallbacks = true;
+ archiveBrowser.DidFindService += new NetServiceBrowser.ServiceFound(didFindPlayers);
+ archiveBrowser.DidRemoveService += new NetServiceBrowser.ServiceRemoved(didRemovePlayers);
+ archiveBrowser.SearchForService(Shared.DisplayCastGlobals.ARCHIVER, Shared.DisplayCastGlobals.BONJOURDOMAIN);
+ }
+ }
+}
View
1,044 ControllerServiceInstaller/ControllerServiceInstaller.vdproj
@@ -0,0 +1,1044 @@
+"DeployProject"
+{
+"VSVersion" = "3:800"
+"ProjectType" = "8:{978C614F-708E-4E1A-B201-565925725DBA}"
+"IsWebType" = "8:FALSE"
+"ProjectName" = "8:ControllerService Installer"
+"LanguageId" = "3:1033"
+"CodePage" = "3:1252"
+"UILanguageId" = "3:1033"
+"SccProjectName" = "8:Svn"
+"SccLocalPath" = "8:Svn"
+"SccAuxPath" = "8:Svn"
+"SccProvider" = "8:SubversionScc"
+ "Hierarchy"
+ {
+ "Entry"
+ {
+ "MsmKey" = "8:_028B53805BEFD78E39B34B67AD029872"
+ "OwnerKey" = "8:_18BDF268BD414065B1E6162DE3E83D40"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_0CC7E99B4600416DB71201F08D3A015A"
+ "OwnerKey" = "8:_UNDEFINED"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_18BDF268BD414065B1E6162DE3E83D40"
+ "OwnerKey" = "8:_UNDEFINED"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_20185EA51E904059BE80B9FE309F35F9"
+ "OwnerKey" = "8:_UNDEFINED"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_4F5B113252E1472C9376A4E27EF319FC"
+ "OwnerKey" = "8:_UNDEFINED"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_5D1347029A484C7CBAEDC882AB7F1AB7"
+ "OwnerKey" = "8:_UNDEFINED"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_6848093FA90498F39C65A41EA7144314"
+ "OwnerKey" = "8:_18BDF268BD414065B1E6162DE3E83D40"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_6848093FA90498F39C65A41EA7144314"
+ "OwnerKey" = "8:_F8B742EEF37D44A69026BC96AD208241"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_F8B742EEF37D44A69026BC96AD208241"
+ "OwnerKey" = "8:_UNDEFINED"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_UNDEFINED"
+ "OwnerKey" = "8:_18BDF268BD414065B1E6162DE3E83D40"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_UNDEFINED"
+ "OwnerKey" = "8:_F8B742EEF37D44A69026BC96AD208241"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_UNDEFINED"
+ "OwnerKey" = "8:_5D1347029A484C7CBAEDC882AB7F1AB7"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_UNDEFINED"
+ "OwnerKey" = "8:_6848093FA90498F39C65A41EA7144314"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ "Entry"
+ {
+ "MsmKey" = "8:_UNDEFINED"
+ "OwnerKey" = "8:_028B53805BEFD78E39B34B67AD029872"
+ "MsmSig" = "8:_UNDEFINED"
+ }
+ }
+ "Configurations"
+ {
+ "Debug"
+ {
+ "DisplayName" = "8:Debug"
+ "IsDebugOnly" = "11:TRUE"
+ "IsReleaseOnly" = "11:FALSE"
+ "OutputFilename" = "8:..\\Installers\\Controller Service\\ControllerServiceInstaller.msi"
+ "PackageFilesAs" = "3:2"
+ "PackageFileSize" = "3:-2147483648"
+ "CabType" = "3:1"
+ "Compression" = "3:2"
+ "SignOutput" = "11:FALSE"
+ "CertificateFile" = "8:"
+ "PrivateKeyFile" = "8:"
+ "TimeStampServer" = "8:"
+ "InstallerBootstrapper" = "3:2"
+ "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}"
+ {
+ "Enabled" = "11:TRUE"
+ "PromptEnabled" = "11:TRUE"
+ "PrerequisitesLocation" = "2:1"
+ "Url" = "8:"
+ "ComponentsUrl" = "8:"
+ "Items"
+ {
+ "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.0,Profile=Client"
+ {
+ "Name" = "8:Microsoft .NET Framework 4 Client Profile (x86 and x64)"
+ "ProductCode" = "8:.NETFramework,Version=v4.0,Profile=Client"
+ }
+ "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:Microsoft.Windows.Installer.3.1"
+ {
+ "Name" = "8:Windows Installer 3.1"
+ "ProductCode" = "8:Microsoft.Windows.Installer.3.1"
+ }
+ }
+ }
+ }
+ "Release"
+ {
+ "DisplayName" = "8:Release"
+ "IsDebugOnly" = "11:FALSE"
+ "IsReleaseOnly" = "11:TRUE"
+ "OutputFilename" = "8:\\\\DATA2\\software\\DisplayCast\\Controller Service - Win7\\DisplayCastControlAPIInstaller.msi"
+ "PackageFilesAs" = "3:2"
+ "PackageFileSize" = "3:-2147483648"
+ "CabType" = "3:1"
+ "Compression" = "3:2"
+ "SignOutput" = "11:FALSE"
+ "CertificateFile" = "8:"
+ "PrivateKeyFile" = "8:"
+ "TimeStampServer" = "8:"
+ "InstallerBootstrapper" = "3:2"
+ "BootstrapperCfg:{63ACBE69-63AA-4F98-B2B6-99F9E24495F2}"
+ {
+ "Enabled" = "11:TRUE"
+ "PromptEnabled" = "11:TRUE"
+ "PrerequisitesLocation" = "2:1"
+ "Url" = "8:"
+ "ComponentsUrl" = "8:"
+ "Items"
+ {
+ "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.0"
+ {
+ "Name" = "8:Microsoft .NET Framework 4 (x86 and x64)"
+ "ProductCode" = "8:.NETFramework,Version=v4.0"
+ }
+ "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:Microsoft.Windows.Installer.3.1"
+ {
+ "Name" = "8:Windows Installer 3.1"
+ "ProductCode" = "8:Microsoft.Windows.Installer.3.1"
+ }
+ }
+ }
+ }
+ }
+ "Deployable"
+ {
+ "CustomAction"
+ {
+ "{4AA51A2D-7D85-4A59-BA75-B0809FC8B380}:_07C3000FE8934C77A685D86141E5E563"
+ {
+ "Name" = "8:Primary output from ControllerService (Release x86)"
+ "Condition" = "8:"
+ "Object" = "8:_18BDF268BD414065B1E6162DE3E83D40"
+ "FileType" = "3:2"
+ "InstallAction" = "3:1"
+ "Arguments" = "8:"
+ "EntryPoint" = "8:"
+ "Sequence" = "3:1"
+ "Identifier" = "8:_53BEC173_8DBE_4889_929C_31E49A421DB9"
+ "InstallerClass" = "11:TRUE"
+ "CustomActionData" = "8:"
+ }
+ "{4AA51A2D-7D85-4A59-BA75-B0809FC8B380}:_3053E36856094F9AB542B53733863549"
+ {
+ "Name" = "8:Primary output from ControllerService (Release x86)"
+ "Condition" = "8:"
+ "Object" = "8:_18BDF268BD414065B1E6162DE3E83D40"
+ "FileType" = "3:2"
+ "InstallAction" = "3:2"
+ "Arguments" = "8:"
+ "EntryPoint" = "8:"
+ "Sequence" = "3:1"
+ "Identifier" = "8:_CFC3A3F2_9A17_477A_AF5E_E5865AA1C908"
+ "InstallerClass" = "11:TRUE"
+ "CustomActionData" = "8:"
+ }
+ "{4AA51A2D-7D85-4A59-BA75-B0809FC8B380}:_3E8C01C282DC40E3951509551E74B038"
+ {
+ "Name" = "8:Primary output from ControllerService (Release x86)"
+ "Condition" = "8:"
+ "Object" = "8:_18BDF268BD414065B1E6162DE3E83D40"
+ "FileType" = "3:2"
+ "InstallAction" = "3:4"
+ "Arguments" = "8:"
+ "EntryPoint" = "8:"
+ "Sequence" = "3:1"
+ "Identifier" = "8:_46110130_C8FD_4F0F_AD7B_B8EE54121335"
+ "InstallerClass" = "11:TRUE"
+ "CustomActionData" = "8:"
+ }
+ "{4AA51A2D-7D85-4A59-BA75-B0809FC8B380}:_A2034D2889D6433299A938E10741BC69"
+ {
+ "Name" = "8:Primary output from ControllerService (Release x86)"
+ "Condition" = "8:"
+ "Object" = "8:_18BDF268BD414065B1E6162DE3E83D40"
+ "FileType" = "3:2"
+ "InstallAction" = "3:3"
+ "Arguments" = "8:"
+ "EntryPoint" = "8:"
+ "Sequence" = "3:1"
+ "Identifier" = "8:_1928416B_1FD2_4F5F_8CA1_CB544C207959"
+ "InstallerClass" = "11:TRUE"
+ "CustomActionData" = "8:"
+ }
+ }
+ "DefaultFeature"
+ {
+ "Name" = "8:DefaultFeature"
+ "Title" = "8:"
+ "Description" = "8:"
+ }
+ "ExternalPersistence"
+ {
+ "LaunchCondition"
+ {
+ "{A06ECF26-33A3-4562-8140-9B0E340D4F24}:_6767B527295B4BC0B33E9ADAE3413C1E"
+ {
+ "Name" = "8:.NET Framework"
+ "Message" = "8:[VSDNETMSG]"
+ "FrameworkVersion" = "8:.NETFramework,Version=v4.0"
+ "AllowLaterVersions" = "11:FALSE"
+ "InstallUrl" = "8:http://go.microsoft.com/fwlink/?LinkId=131000"
+ }
+ }
+ }
+ "File"
+ {
+ "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_028B53805BEFD78E39B34B67AD029872"
+ {
+ "AssemblyRegister" = "3:1"
+ "AssemblyIsInGAC" = "11:FALSE"
+ "AssemblyAsmDisplayName" = "8:Shared, Version=1.0.0.0, Culture=neutral, processorArchitecture=x86"
+ "ScatterAssemblies"
+ {
+ "_028B53805BEFD78E39B34B67AD029872"
+ {
+ "Name" = "8:Shared.dll"
+ "Attributes" = "3:512"
+ }
+ }
+ "SourcePath" = "8:Shared.dll"
+ "TargetName" = "8:"
+ "Tag" = "8:"
+ "Folder" = "8:_B8E7A3D603504747861E2C04F35AA9EB"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Vital" = "11:TRUE"
+ "ReadOnly" = "11:FALSE"
+ "Hidden" = "11:FALSE"
+ "System" = "11:FALSE"
+ "Permanent" = "11:FALSE"
+ "SharedLegacy" = "11:FALSE"
+ "PackageAs" = "3:1"
+ "Register" = "3:1"
+ "Exclude" = "11:TRUE"
+ "IsDependency" = "11:TRUE"
+ "IsolateTo" = "8:"
+ }
+ "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0CC7E99B4600416DB71201F08D3A015A"
+ {
+ "SourcePath" = "8:..\\Shared\\license.rtf"
+ "TargetName" = "8:license.rtf"
+ "Tag" = "8:"
+ "Folder" = "8:_B8E7A3D603504747861E2C04F35AA9EB"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Vital" = "11:TRUE"
+ "ReadOnly" = "11:FALSE"
+ "Hidden" = "11:FALSE"
+ "System" = "11:FALSE"
+ "Permanent" = "11:FALSE"
+ "SharedLegacy" = "11:FALSE"
+ "PackageAs" = "3:1"
+ "Register" = "3:1"
+ "Exclude" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "IsolateTo" = "8:"
+ }
+ "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_20185EA51E904059BE80B9FE309F35F9"
+ {
+ "SourcePath" = "8:..\\Shared\\displayCast-BASIC-LOGO.jpg"
+ "TargetName" = "8:displayCast-BASIC-LOGO.jpg"
+ "Tag" = "8:"
+ "Folder" = "8:_B8E7A3D603504747861E2C04F35AA9EB"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Vital" = "11:TRUE"
+ "ReadOnly" = "11:FALSE"
+ "Hidden" = "11:FALSE"
+ "System" = "11:FALSE"
+ "Permanent" = "11:FALSE"
+ "SharedLegacy" = "11:FALSE"
+ "PackageAs" = "3:1"
+ "Register" = "3:1"
+ "Exclude" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "IsolateTo" = "8:"
+ }
+ "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4F5B113252E1472C9376A4E27EF319FC"
+ {
+ "SourcePath" = "8:..\\Shared\\dc.ico"
+ "TargetName" = "8:dc.ico"
+ "Tag" = "8:"
+ "Folder" = "8:_B8E7A3D603504747861E2C04F35AA9EB"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Vital" = "11:TRUE"
+ "ReadOnly" = "11:FALSE"
+ "Hidden" = "11:FALSE"
+ "System" = "11:FALSE"
+ "Permanent" = "11:FALSE"
+ "SharedLegacy" = "11:FALSE"
+ "PackageAs" = "3:1"
+ "Register" = "3:1"
+ "Exclude" = "11:FALSE"
+ "IsDependency" = "11:FALSE"
+ "IsolateTo" = "8:"
+ }
+ "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_6848093FA90498F39C65A41EA7144314"
+ {
+ "AssemblyRegister" = "3:1"
+ "AssemblyIsInGAC" = "11:FALSE"
+ "AssemblyAsmDisplayName" = "8:ZeroconfService, Version=0.6.0.0, Culture=neutral, processorArchitecture=x86"
+ "ScatterAssemblies"
+ {
+ "_6848093FA90498F39C65A41EA7144314"
+ {
+ "Name" = "8:ZeroconfService.dll"
+ "Attributes" = "3:512"
+ }
+ }
+ "SourcePath" = "8:ZeroconfService.dll"
+ "TargetName" = "8:"
+ "Tag" = "8:"
+ "Folder" = "8:_B8E7A3D603504747861E2C04F35AA9EB"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Vital" = "11:TRUE"
+ "ReadOnly" = "11:FALSE"
+ "Hidden" = "11:FALSE"
+ "System" = "11:FALSE"
+ "Permanent" = "11:FALSE"
+ "SharedLegacy" = "11:FALSE"
+ "PackageAs" = "3:1"
+ "Register" = "3:1"
+ "Exclude" = "11:TRUE"
+ "IsDependency" = "11:TRUE"
+ "IsolateTo" = "8:"
+ }
+ }
+ "FileType"
+ {
+ }
+ "Folder"
+ {
+ "{3C67513D-01DD-4637-8A68-80971EB9504F}:_B8E7A3D603504747861E2C04F35AA9EB"
+ {
+ "DefaultLocation" = "8:[ProgramFilesFolder][Manufacturer]\\[ProductName]"
+ "Name" = "8:#1925"
+ "AlwaysCreate" = "11:FALSE"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Property" = "8:TARGETDIR"
+ "Folders"
+ {
+ }
+ }
+ "{1525181F-901A-416C-8A58-119130FE478E}:_D1A59A85B6CF46A1BB19C53EDEE90ADB"
+ {
+ "Name" = "8:#1919"
+ "AlwaysCreate" = "11:FALSE"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Property" = "8:ProgramMenuFolder"
+ "Folders"
+ {
+ }
+ }
+ "{1525181F-901A-416C-8A58-119130FE478E}:_E01330328BD0469B9BE92746071B51AA"
+ {
+ "Name" = "8:#1916"
+ "AlwaysCreate" = "11:FALSE"
+ "Condition" = "8:"
+ "Transitive" = "11:FALSE"
+ "Property" = "8:DesktopFolder"
+ "Folders"
+ {
+ }
+ }
+ }
+ "LaunchCondition"
+ {
+ }
+ "Locator"
+ {
+ }
+ "MsiBootstrapper"
+ {
+ "LangId" = "3:1033"
+ "RequiresElevation" = "11:FALSE"
+ }
+ "Product"
+ {
+ "Name" = "8:Microsoft Visual Studio"
+ "ProductName" = "8:FXPAL DisplayCast Controller"
+ "ProductCode" = "8:{4D8DAE5B-7003-43E7-B74B-9B5BA7DD80F4}"
+ "PackageCode" = "8:{5996BAA3-3D0D-493F-B32F-B4964486EFF6}"
+ "UpgradeCode" = "8:{0F9C274A-BB55-4DC6-A41E-28B7DF930346}"
+ "AspNetVersion" = "8:4.0.30319.0"
+ "RestartWWWService" = "11:FALSE"
+ "RemovePreviousVersions" = "11:TRUE"
+ "DetectNewerInstalledVersion" = "11:TRUE"
+ "InstallAllUsers" = "11:TRUE"
+ "ProductVersion" = "8:1.0.4"
+ "Manufacturer" = "8:FX Palo Alto Laboratory Inc."
+ "ARPHELPTELEPHONE" = "8:"
+ "ARPHELPLINK" = "8:http://www.fxpal.com/?p=DisplayCast"
+ "Title" = "8:FXPAL DisplayCast Controller"
+ "Subject" = "8:"
+ "ARPCONTACT" = "8:Surendar Chandra"
+ "Keywords" = "8:"
+ "ARPCOMMENTS" = "8:FXPAL DisplayCast Controller"
+ "ARPURLINFOABOUT" = "8:http://www.fxpal.com/"
+ "ARPPRODUCTICON" = "8:"
+ "ARPIconIndex" = "3:0"
+ "SearchPath" = "8:"
+ "UseSystemSearchPath" = "11:TRUE"
+ "TargetPlatform" = "3:0"
+ "PreBuildEvent" = "8:"
+ "PostBuildEvent" = "8:"
+ "RunPostBuildEvent" = "3:0"
+ }
+ "Registry"
+ {
+ "HKLM"
+ {
+ "Keys"
+ {
+ "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_5F64EEE270F940DB800C7E32468D822B"
+ {
+ "Name" = "8:Software"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_E19CD13E0502427A8E17AFF5277B9175"
+ {
+ "Name" = "8:[Manufacturer]"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ }
+ "HKCU"
+ {
+ "Keys"
+ {
+ "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_FDB361A64AB74683B9307A42A4A332F7"
+ {
+ "Name" = "8:Software"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ "{60EA8692-D2D5-43EB-80DC-7906BF13D6EF}:_B59BFBF9EC6F43DC855F72F62245AA87"
+ {
+ "Name" = "8:[Manufacturer]"
+ "Condition" = "8:"
+ "AlwaysCreate" = "11:FALSE"
+ "DeleteAtUninstall" = "11:FALSE"
+ "Transitive" = "11:FALSE"
+ "Keys"
+ {
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ "Values"
+ {
+ }
+ }
+ }
+ }
+ "HKCR"
+ {
+ "Keys"
+ {
+ }
+ }
+ "HKU"
+ {
+ "Keys"
+ {
+ }
+ }
+ "HKPU"
+ {
+ "Keys"
+ {
+ }
+ }
+ }
+ "Sequences"
+ {
+ }
+ "Shortcut"
+ {
+ }
+ "UserInterface"