SharpDisplay: Migrating to new robust client scheme. MiniDisplay
authorStephaneLenclud
Thu, 01 Jan 2015 23:35:49 +0100
branchMiniDisplay
changeset 447af40a92d480d
parent 446 0cb7b9f6a6f8
child 448 10c04c79527e
SharpDisplay: Migrating to new robust client scheme.
GUI/SharpDisplay.cs
GUI/SharpDisplayClient.cs
GUI/SharpDisplayInterface.cs
OpenHardwareMonitor.csproj
     1.1 --- a/GUI/SharpDisplay.cs	Mon Sep 22 21:59:11 2014 +0200
     1.2 +++ b/GUI/SharpDisplay.cs	Thu Jan 01 23:35:49 2015 +0100
     1.3 @@ -25,19 +25,19 @@
     1.4  
     1.5  namespace OpenHardwareMonitor.GUI
     1.6  {
     1.7 -    public class SharpDisplay : ICallback, IDisposable
     1.8 +    public class SharpDisplay : IDisposable
     1.9      {
    1.10          private IComputer computer;
    1.11          private PersistentSettings settings;
    1.12          private UnitManager unitManager;
    1.13          private List<SensorSharpDisplay> iSensors = new List<SensorSharpDisplay>();
    1.14 -        private global::SharpDisplay.Client iClient;
    1.15 -        TextField iTextFieldTop;
    1.16 -        TextField iTextFieldBottom;
    1.17 -        TextField iTextFieldTopRight;
    1.18 -        TextField iTextFieldBottomRight;
    1.19 +        private global::SharpDisplay.DisplayClient iClient;
    1.20 +        DataField iTextFieldTop;
    1.21 +		DataField iTextFieldBottom;
    1.22 +		DataField iTextFieldTopRight;
    1.23 +		DataField iTextFieldBottomRight;
    1.24  
    1.25 -        TextField[] iTextFields;
    1.26 +		DataField[] iTextFields;
    1.27  
    1.28          private int iNextSensorToDisplay = 0;
    1.29          private int iTickCounter = 0;
    1.30 @@ -53,32 +53,18 @@
    1.31  
    1.32              //Connect our client
    1.33              //Instance context is then managed by our client class
    1.34 -            iClient = new global::SharpDisplay.Client(this);
    1.35 +            iClient = new global::SharpDisplay.DisplayClient();
    1.36              //
    1.37 -            iTextFieldTop = new TextField(0);
    1.38 -            iTextFieldBottom = new TextField(1);
    1.39 -            iTextFieldTopRight = new TextField(2, "", ContentAlignment.MiddleRight);
    1.40 -            iTextFieldBottomRight = new TextField(3, "", ContentAlignment.MiddleRight);
    1.41 -
    1.42 -            iTextFields = new TextField[] { iTextFieldTop, iTextFieldBottom };
    1.43 -
    1.44 +			iTextFieldTop = new DataField(0);
    1.45 +			iTextFieldBottom = new DataField(1);
    1.46 +			iTextFieldTopRight = new DataField(2, "", ContentAlignment.MiddleRight);
    1.47 +			iTextFieldBottomRight = new DataField(3, "", ContentAlignment.MiddleRight);
    1.48 +			//
    1.49 +			iClient.Open();
    1.50 +			iClient.SetName("Open Hardware Monitor");
    1.51 +			CreateFields();
    1.52          }
    1.53  
    1.54 -        //From ICallback
    1.55 -        public void OnConnected()
    1.56 -        {
    1.57 -            //Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
    1.58 -            //Trace.WriteLine("Callback thread = " + Thread.CurrentThread.ManagedThreadId);
    1.59 -            //MessageBox.Show("OnConnected()", "Client");
    1.60 -        }
    1.61 -
    1.62 -        //From ICallback
    1.63 -        public void OnCloseOrder()
    1.64 -        {
    1.65 -            iClient.Close();
    1.66 -        }
    1.67 -
    1.68 -
    1.69          private void HardwareRemoved(IHardware hardware)
    1.70          {
    1.71              hardware.SensorAdded -= new SensorEventHandler(SensorAdded);
    1.72 @@ -123,6 +109,27 @@
    1.73  
    1.74          }
    1.75  
    1.76 +		private void CreateFields()
    1.77 +		{
    1.78 +			if (iPacked)
    1.79 +            {
    1.80 +                //We just switched to packed mode                    
    1.81 +                //Make sure our layout is proper
    1.82 +                TableLayout layout = new TableLayout(2, 2);
    1.83 +                iClient.SetLayout(layout);
    1.84 +				iTextFields = new DataField[] { iTextFieldTop, iTextFieldBottom, iTextFieldTopRight, iTextFieldBottomRight };
    1.85 +				iClient.CreateFields(iTextFields);
    1.86 +            }
    1.87 +            else
    1.88 +            {
    1.89 +                //Non packed mode
    1.90 +                TableLayout layout = new TableLayout(1, 2);
    1.91 +                iClient.SetLayout(layout);
    1.92 +				iTextFields = new DataField[] { iTextFieldTop, iTextFieldBottom };
    1.93 +				iClient.CreateFields(iTextFields);
    1.94 +            }
    1.95 +		}
    1.96 +
    1.97          public void Redraw(bool aPacked, bool aDisplayTime)
    1.98          {
    1.99              const int KNumberOfTickBeforeSwitch = 4;
   1.100 @@ -141,19 +148,8 @@
   1.101                      //Remember mode
   1.102                      iPacked = aPacked;
   1.103  
   1.104 -                    if (iPacked)
   1.105 -                    {
   1.106 -                        //We just switched to packed mode                    
   1.107 -                        //Make sure our layout is proper
   1.108 -                        TableLayout layout = new TableLayout(2, 2);
   1.109 -                        iClient.SetLayout(layout);
   1.110 -                    }
   1.111 -                    else
   1.112 -                    {
   1.113 -                        //Non packed mode
   1.114 -                        TableLayout layout = new TableLayout(1, 2);
   1.115 -                        iClient.SetLayout(layout);
   1.116 -                    }
   1.117 +					CreateFields();
   1.118 +
   1.119                  }
   1.120              }
   1.121  
   1.122 @@ -169,7 +165,7 @@
   1.123                      //First slot is taken by time display
   1.124                      count++;
   1.125                      iTextFieldTop.Text = time;
   1.126 -                    iClient.SetText(iTextFieldTop);
   1.127 +                    iClient.SetField(iTextFieldTop);
   1.128                  }
   1.129  
   1.130                  if (aPacked)
   1.131 @@ -180,22 +176,22 @@
   1.132                      if (count == 1)
   1.133                      {
   1.134                          iTextFieldTop.Text = packedText;
   1.135 -                        iClient.SetText(iTextFieldTop);
   1.136 +						iClient.SetField(iTextFieldTop);
   1.137                      }
   1.138                      else if (count == 2)
   1.139                      {
   1.140                          iTextFieldBottom.Text = packedText;
   1.141 -                        iClient.SetText(iTextFieldBottom);
   1.142 +						iClient.SetField(iTextFieldBottom);
   1.143                      }
   1.144                      else if (count == 3)
   1.145                      {
   1.146                          iTextFieldTopRight.Text = packedText;
   1.147 -                        iClient.SetText(iTextFieldTopRight);
   1.148 +						iClient.SetField(iTextFieldTopRight);
   1.149                      }
   1.150                      else if (count == 4)
   1.151                      {
   1.152                          iTextFieldBottomRight.Text = packedText;
   1.153 -                        iClient.SetText(iTextFieldBottomRight);
   1.154 +						iClient.SetField(iTextFieldBottomRight);
   1.155                      }
   1.156                  }
   1.157              }
   1.158 @@ -345,7 +341,7 @@
   1.159              //iServer.SendMessage("set-vfd-text:" + aUpperLine + "\n" + aLowerLine);
   1.160              iTextFieldTop.Text = aUpperLine;
   1.161              iTextFieldBottom.Text = aLowerLine;
   1.162 -            iClient.SetTexts(iTextFields);
   1.163 +            iClient.SetFields(iTextFields);
   1.164  
   1.165          }
   1.166  
     2.1 --- a/GUI/SharpDisplayClient.cs	Mon Sep 22 21:59:11 2014 +0200
     2.2 +++ b/GUI/SharpDisplayClient.cs	Thu Jan 01 23:35:49 2015 +0100
     2.3 @@ -1,207 +1,64 @@
     2.4 -/*
     2.5 - 
     2.6 -  This Source Code Form is subject to the terms of the Mozilla Public
     2.7 -  License, v. 2.0. If a copy of the MPL was not distributed with this
     2.8 -  file, You can obtain one at http://mozilla.org/MPL/2.0/.
     2.9 - 
    2.10 -  Copyright (C) 2009-2012 Michael Möller <mmoeller@openhardwaremonitor.org>
    2.11 -	
    2.12 -*/
    2.13 -
    2.14 +
    2.15  using System;
    2.16  using System.Collections.Generic;
    2.17 -using System.Drawing;
    2.18 +using System.Linq;
    2.19  using System.Text;
    2.20 -using System.Diagnostics;
    2.21 +using System.Threading.Tasks;
    2.22  using System.Windows.Forms;
    2.23 -using System.Windows;
    2.24 -using OpenHardwareMonitor.Hardware;
    2.25 -using OpenHardwareMonitor.Utilities;
    2.26 -using System.Runtime.InteropServices;
    2.27 -using UacHelpers;
    2.28 -//
    2.29 +using SharpDisplay;
    2.30  using System.ServiceModel;
    2.31 -using System.Runtime.Serialization;
    2.32 -using SharpDisplay;
    2.33 +using System.ServiceModel.Channels;
    2.34 +
    2.35  
    2.36  namespace SharpDisplay
    2.37  {
    2.38 -
    2.39 -
    2.40 -
    2.41 -    /// <summary>
    2.42 -    /// For client to specify a specific layout.
    2.43 -    /// </summary>
    2.44 -    [DataContract]
    2.45 -    public class TableLayout
    2.46 -    {
    2.47 -        public TableLayout()
    2.48 -        {
    2.49 -            Columns = new List<ColumnStyle>();
    2.50 -            Rows = new List<RowStyle>();
    2.51 -            Cells = new List<DataField>();
    2.52 -        }
    2.53 -
    2.54 -        public TableLayout(int aColumnCount, int aRowCount)
    2.55 -        {
    2.56 -            Columns = new List<ColumnStyle>();
    2.57 -            Rows = new List<RowStyle>();
    2.58 -
    2.59 -            for (int i = 0; i < aColumnCount; i++)
    2.60 -            {
    2.61 -                Columns.Add(new ColumnStyle(SizeType.Percent, 100 / aColumnCount));
    2.62 -            }
    2.63 -
    2.64 -            for (int i = 0; i < aRowCount; i++)
    2.65 -            {
    2.66 -                Rows.Add(new RowStyle(SizeType.Percent, 100 / aRowCount));
    2.67 -            }
    2.68 -        }
    2.69 -
    2.70 -        [DataMember]
    2.71 -        public List<DataField> Cells { get; set; }
    2.72 -
    2.73 -        [DataMember]
    2.74 -        public List<ColumnStyle> Columns { get; set; }
    2.75 -
    2.76 -        [DataMember]
    2.77 -        public List<RowStyle> Rows { get; set; }
    2.78 -
    2.79 -    }
    2.80 -
    2.81      /// <summary>
    2.82      ///
    2.83      /// </summary>
    2.84 -    [DataContract]
    2.85 -    public class DataField
    2.86 +    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
    2.87 +    public class Callback : ICallback, IDisposable
    2.88      {
    2.89 -        [DataMember]
    2.90 -        public int Column { get; set; }
    2.91 +		private DisplayClient DisplayClient { get; set; }
    2.92  
    2.93 -        [DataMember]
    2.94 -        public int Row { get; set; }
    2.95 +		public Callback(DisplayClient aClient)
    2.96 +        {
    2.97 +            DisplayClient = aClient;
    2.98 +        }
    2.99  
   2.100 -        [DataMember]
   2.101 -        public int ColumnSpan { get; set; }
   2.102 +        public void OnConnected()
   2.103 +        {
   2.104 +            //Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
   2.105 +            //Trace.WriteLine("Callback thread = " + Thread.CurrentThread.ManagedThreadId);
   2.106  
   2.107 -        [DataMember]
   2.108 -        public int RowSpan { get; set; }
   2.109 +            MessageBox.Show("OnConnected()", "Client");
   2.110 +        }
   2.111  
   2.112 +
   2.113 +        public void OnCloseOrder()
   2.114 +        {
   2.115 +			DisplayClient.Close();
   2.116 +            //Debug.Assert(Thread.CurrentThread.IsThreadPoolThread);
   2.117 +            //Trace.WriteLine("Callback thread = " + Thread.CurrentThread.ManagedThreadId);
   2.118 +
   2.119 +            //MessageBox.Show("OnServerClosing()", "Client");
   2.120 +            //MainForm.CloseConnectionThreadSafe();
   2.121 +            //MainForm.CloseThreadSafe();
   2.122 +        }
   2.123 +
   2.124 +        //From IDisposable
   2.125 +        public void Dispose()
   2.126 +        {
   2.127 +
   2.128 +        }
   2.129      }
   2.130  
   2.131  
   2.132      /// <summary>
   2.133 -    /// TextField can be send to our server to be displayed on the screen.
   2.134 -    /// </summary>
   2.135 -    [DataContract]
   2.136 -    public class TextField : DataField
   2.137 -    {
   2.138 -        public TextField()
   2.139 -        {
   2.140 -            Index = 0;
   2.141 -            Text = "";
   2.142 -            Alignment = ContentAlignment.MiddleLeft;
   2.143 -        }
   2.144 -
   2.145 -        public TextField(int aIndex, string aText = "", ContentAlignment aAlignment = ContentAlignment.MiddleLeft)
   2.146 -        {
   2.147 -            Index = aIndex;
   2.148 -            Text = aText;
   2.149 -            Alignment = aAlignment;
   2.150 -        }
   2.151 -
   2.152 -        [DataMember]
   2.153 -        public int Index { get; set; }
   2.154 -
   2.155 -        [DataMember]
   2.156 -        public string Text { get; set; }
   2.157 -
   2.158 -        [DataMember]
   2.159 -        public ContentAlignment Alignment { get; set; }
   2.160 -    }
   2.161 -
   2.162 -    /// <summary>
   2.163 -    /// Define our SharpDisplay service.
   2.164 -    /// Clients and servers must implement it to communicate with one another.
   2.165 -    /// Through this service clients can send requests to a server.
   2.166 -    /// Through this service a server session can receive requests from a client.
   2.167 -    /// </summary>
   2.168 -    [ServiceContract(CallbackContract = typeof(ICallback), SessionMode = SessionMode.Required)]
   2.169 -    public interface IService
   2.170 -    {
   2.171 -        /// <summary>
   2.172 -        /// Set the name of this client.
   2.173 -        /// Name is a convenient way to recognize your client.
   2.174 -        /// Naming you client is not mandatory.
   2.175 -        /// In the absence of a name the session ID is often used instead.
   2.176 -        /// </summary>
   2.177 -        /// <param name="aClientName"></param>
   2.178 -        [OperationContract(IsOneWay = true)]
   2.179 -        void SetName(string aClientName);
   2.180 -
   2.181 -
   2.182 -        /// <summary>
   2.183 -        /// </summary>
   2.184 -        /// <param name="aLayout"></param>
   2.185 -        [OperationContract(IsOneWay = true)]
   2.186 -        void SetLayout(TableLayout aLayout);
   2.187 -
   2.188 -        /// <summary>
   2.189 -        /// Put the given text in the given field on your display.
   2.190 -        /// Fields are often just lines of text.
   2.191 -        /// </summary>
   2.192 -        /// <param name="aTextFieldIndex"></param>
   2.193 -        [OperationContract(IsOneWay = true)]
   2.194 -        void SetText(TextField aTextField);
   2.195 -
   2.196 -        /// <summary>
   2.197 -        /// Allows a client to set multiple text fields at once.
   2.198 -        /// </summary>
   2.199 -        /// <param name="aTexts"></param>
   2.200 -        [OperationContract(IsOneWay = true)]
   2.201 -        void SetTexts(System.Collections.Generic.IList<TextField> aTextFields);
   2.202 -
   2.203 -        /// <summary>
   2.204 -        /// Provides the number of clients currently connected
   2.205 -        /// </summary>
   2.206 -        /// <returns></returns>
   2.207 -        [OperationContract()]
   2.208 -        int ClientCount();
   2.209 -
   2.210 -    }
   2.211 -
   2.212 -    /// <summary>
   2.213 -    /// SharDisplay callback provides a means for a server to notify its clients.
   2.214 -    /// </summary>
   2.215 -    public interface ICallback
   2.216 -    {
   2.217 -        [OperationContract(IsOneWay = true)]
   2.218 -        void OnConnected();
   2.219 -
   2.220 -        /// <summary>
   2.221 -        /// Tell our client to close its connection.
   2.222 -        /// Notably sent when the server is shutting down.
   2.223 -        /// </summary>
   2.224 -        [OperationContract(IsOneWay = true)]
   2.225 -        void OnCloseOrder();
   2.226 -    }
   2.227 -
   2.228 -
   2.229 -
   2.230 -}
   2.231 -
   2.232 -
   2.233 -
   2.234 -namespace SharpDisplay
   2.235 -{
   2.236 -
   2.237 -    /// <summary>
   2.238      ///
   2.239      /// </summary>
   2.240      [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
   2.241      public class Client : DuplexClientBase<IService>
   2.242      {
   2.243 -        public string Name { get; set; }
   2.244          public string SessionId { get { return InnerChannel.SessionId; } }
   2.245  
   2.246          public Client(ICallback aCallback)
   2.247 @@ -210,7 +67,6 @@
   2.248  
   2.249          public void SetName(string aClientName)
   2.250          {
   2.251 -            Name = aClientName;
   2.252              Channel.SetName(aClientName);
   2.253          }
   2.254  
   2.255 @@ -219,14 +75,14 @@
   2.256              Channel.SetLayout(aLayout);
   2.257          }
   2.258  
   2.259 -        public void SetText(TextField aTextField)
   2.260 +        public void SetField(DataField aField)
   2.261          {
   2.262 -            Channel.SetText(aTextField);
   2.263 +            Channel.SetField(aField);
   2.264          }
   2.265  
   2.266 -        public void SetTexts(System.Collections.Generic.IList<TextField> aTextFields)
   2.267 +        public void SetFields(System.Collections.Generic.IList<DataField> aFields)
   2.268          {
   2.269 -            Channel.SetTexts(aTextFields);
   2.270 +            Channel.SetFields(aFields);
   2.271          }
   2.272  
   2.273          public int ClientCount()
   2.274 @@ -236,10 +92,200 @@
   2.275  
   2.276          public bool IsReady()
   2.277          {
   2.278 -            return State == CommunicationState.Opened;
   2.279 +            return State == CommunicationState.Opened || State == CommunicationState.Created;
   2.280          }
   2.281 +    }
   2.282 +
   2.283 +
   2.284 +    /// <summary>
   2.285 +    /// Handle connection with our Sharp Display Server.
   2.286 +    /// If the connection is faulted it will attempt to restart it.
   2.287 +    /// </summary>
   2.288 +    public class DisplayClient
   2.289 +    {
   2.290 +        private Client iClient;
   2.291 +        private Callback iCallback;
   2.292 +        private bool resetingConnection = false;
   2.293 +
   2.294 +        //private MainForm MainForm { get; set; }
   2.295 +        public string SessionId { get { return iClient.SessionId; } }
   2.296 +        public string Name { get; private set; }
   2.297 +        private TableLayout Layout { get; set; }
   2.298 +        private System.Collections.Generic.IList<DataField> Fields { get; set; }
   2.299 +
   2.300 +
   2.301 +        public DisplayClient(/*MainForm aMainForm*/)
   2.302 +        {
   2.303 +            //MainForm = aMainForm;
   2.304 +            Name = "";
   2.305 +            Fields = new DataField[]{};
   2.306 +        }
   2.307 +
   2.308 +        /// <summary>
   2.309 +        /// Initialize our server connection.
   2.310 +        /// </summary>
   2.311 +        public void Open()
   2.312 +        {
   2.313 +            iCallback = new Callback(this);
   2.314 +            iClient = new Client(iCallback);
   2.315 +        }
   2.316 +
   2.317 +        /// <summary>
   2.318 +        /// Terminate our server connection.
   2.319 +        /// </summary>
   2.320 +        public void Close()
   2.321 +        {
   2.322 +            iClient.Close();
   2.323 +            iClient = null;
   2.324 +            iCallback.Dispose();
   2.325 +            iCallback = null;
   2.326 +        }
   2.327 +
   2.328 +        /// <summary>
   2.329 +        /// Tells whether a server connection is available.
   2.330 +        /// </summary>
   2.331 +        /// <returns>True if a server connection is available. False otherwise.</returns>
   2.332 +        public bool IsReady()
   2.333 +        {
   2.334 +            return (iClient != null && iCallback != null && iClient.IsReady());
   2.335 +        }
   2.336 +
   2.337 +        /// <summary>
   2.338 +        /// Check if our server connection is available and attempt reset it if it isn't.
   2.339 +        /// This is notably dealing with timed out connections.
   2.340 +        /// </summary>
   2.341 +        public void CheckConnection()
   2.342 +        {
   2.343 +            if (!IsReady() && !resetingConnection)
   2.344 +            {
   2.345 +                //Try to reconnect
   2.346 +                Open();
   2.347 +
   2.348 +                //Avoid stack overflow in case of persisting failure
   2.349 +                resetingConnection = true;
   2.350 +
   2.351 +                try
   2.352 +                {
   2.353 +                    //On reconnect there is a bunch of properties we need to reset
   2.354 +                    if (Name != "")
   2.355 +                    {
   2.356 +                        iClient.SetName(Name);
   2.357 +                    }
   2.358 +
   2.359 +                    SetLayout(Layout);
   2.360 +                    iClient.SetFields(Fields);
   2.361 +                }
   2.362 +                finally
   2.363 +                {
   2.364 +                    //Make sure our this state does not get out of sync
   2.365 +                    resetingConnection = true;
   2.366 +                }
   2.367 +            }
   2.368 +        }
   2.369 +
   2.370 +        public void SetName(string aClientName)
   2.371 +        {
   2.372 +            Name = aClientName;
   2.373 +            CheckConnection();
   2.374 +            iClient.SetName(aClientName);
   2.375 +        }
   2.376 +
   2.377 +
   2.378 +        public void SetLayout(TableLayout aLayout)
   2.379 +        {
   2.380 +            Layout = aLayout;
   2.381 +            CheckConnection();
   2.382 +            iClient.SetLayout(aLayout);
   2.383 +        }
   2.384 +
   2.385 +        /// <summary>
   2.386 +        /// Set the specified field.
   2.387 +        /// </summary>
   2.388 +        /// <param name="aField"></param>
   2.389 +        /// <returns>True if the specified field was set client side. False means you need to redefine all your fields using CreateFields.</returns>
   2.390 +        public bool SetField(DataField aField)
   2.391 +        {
   2.392 +            int i = 0;
   2.393 +            bool fieldFound = false;
   2.394 +            foreach (DataField field in Fields)
   2.395 +            {
   2.396 +                if (field.Index == aField.Index)
   2.397 +                {
   2.398 +                    //Update our field then
   2.399 +                    Fields[i] = aField;
   2.400 +                    fieldFound = true;
   2.401 +                    break;
   2.402 +                }
   2.403 +                i++;
   2.404 +            }
   2.405 +
   2.406 +            if (!fieldFound)
   2.407 +            {
   2.408 +                //Field not found, make to use SetFields with all your fields at least once after setting your layout.
   2.409 +                return false;
   2.410 +            }
   2.411 +
   2.412 +            CheckConnection();
   2.413 +            iClient.SetField(aField);
   2.414 +            return true;
   2.415 +        }
   2.416 +
   2.417 +        /// <summary>
   2.418 +        /// Use this function when updating existing fields.
   2.419 +        /// </summary>
   2.420 +        /// <param name="aFields"></param>
   2.421 +        public bool SetFields(System.Collections.Generic.IList<DataField> aFields)
   2.422 +        {
   2.423 +            int fieldFoundCount = 0;
   2.424 +            foreach (DataField fieldUpdate in aFields)
   2.425 +            {
   2.426 +                int i = 0;
   2.427 +                foreach (DataField existingField in Fields)
   2.428 +                {
   2.429 +                    if (existingField.Index == fieldUpdate.Index)
   2.430 +                    {
   2.431 +                        //Update our field then
   2.432 +                        Fields[i] = fieldUpdate;
   2.433 +                        fieldFoundCount++;
   2.434 +                        //Move on to the next field
   2.435 +                        break;
   2.436 +                    }
   2.437 +                    i++;
   2.438 +                }
   2.439 +            }
   2.440 +
   2.441 +            //
   2.442 +            if (fieldFoundCount!=aFields.Count)
   2.443 +            {
   2.444 +                //Field not found, make sure to use SetFields with all your fields at least once after setting your layout.
   2.445 +                return false;
   2.446 +            }
   2.447 +
   2.448 +            CheckConnection();
   2.449 +            iClient.SetFields(aFields);
   2.450 +            return true;
   2.451 +        }
   2.452 +
   2.453 +        /// <summary>
   2.454 +        /// Use this function when creating your fields.
   2.455 +        /// </summary>
   2.456 +        /// <param name="aFields"></param>
   2.457 +        public void CreateFields(System.Collections.Generic.IList<DataField> aFields)
   2.458 +        {
   2.459 +            Fields = aFields;
   2.460 +            CheckConnection();
   2.461 +            iClient.SetFields(aFields);
   2.462 +        }
   2.463 +
   2.464 +        public int ClientCount()
   2.465 +        {
   2.466 +            CheckConnection();
   2.467 +            return iClient.ClientCount();
   2.468 +        }
   2.469 +
   2.470 +
   2.471  
   2.472      }
   2.473  
   2.474  
   2.475 -}
   2.476 \ No newline at end of file
   2.477 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/GUI/SharpDisplayInterface.cs	Thu Jan 01 23:35:49 2015 +0100
     3.3 @@ -0,0 +1,204 @@
     3.4 +//
     3.5 +// Define a public API for both SharpDisplay client and server to use.
     3.6 +//
     3.7 +
     3.8 +using System;
     3.9 +using System.Collections.Generic;
    3.10 +using System.Linq;
    3.11 +using System.Text;
    3.12 +using System.Threading.Tasks;
    3.13 +using System.ServiceModel;
    3.14 +using System.Collections;
    3.15 +using System.Drawing;
    3.16 +using System.Runtime.Serialization;
    3.17 +using System.Windows.Forms;
    3.18 +
    3.19 +
    3.20 +namespace SharpDisplay
    3.21 +{
    3.22 +	/// <summary>
    3.23 +	/// For client to specify a specific layout.
    3.24 +	/// </summary>
    3.25 +	[DataContract]
    3.26 +	public class TableLayout
    3.27 +	{
    3.28 +		public TableLayout()
    3.29 +		{
    3.30 +			Columns = new List<ColumnStyle>();
    3.31 +			Rows = new List<RowStyle>();
    3.32 +			Cells = new List<DataField>();
    3.33 +		}
    3.34 +
    3.35 +		public TableLayout(int aColumnCount, int aRowCount)
    3.36 +		{
    3.37 +			Columns = new List<ColumnStyle>();
    3.38 +			Rows = new List<RowStyle>();
    3.39 +
    3.40 +			for (int i = 0; i < aColumnCount; i++)
    3.41 +			{
    3.42 +				Columns.Add(new ColumnStyle(SizeType.Percent, 100 / aColumnCount));
    3.43 +			}
    3.44 +
    3.45 +			for (int i = 0; i < aRowCount; i++)
    3.46 +			{
    3.47 +				Rows.Add(new RowStyle(SizeType.Percent, 100 / aRowCount));
    3.48 +			}
    3.49 +		}
    3.50 +
    3.51 +		[DataMember]
    3.52 +		public List<DataField> Cells { get; set; }
    3.53 +
    3.54 +		[DataMember]
    3.55 +		public List<ColumnStyle> Columns { get; set; }
    3.56 +
    3.57 +		[DataMember]
    3.58 +		public List<RowStyle> Rows { get; set; }
    3.59 +	}
    3.60 +
    3.61 +	/// <summary>
    3.62 +	///
    3.63 +	/// </summary>
    3.64 +	[DataContract]
    3.65 +	public class DataField
    3.66 +	{
    3.67 +		public DataField()
    3.68 +		{
    3.69 +			Index = 0;
    3.70 +			ColumnSpan = 1;
    3.71 +			RowSpan = 1;
    3.72 +			//Text
    3.73 +			Text = "";
    3.74 +			Alignment = ContentAlignment.MiddleLeft;
    3.75 +			//Bitmap
    3.76 +			Bitmap = null;
    3.77 +		}
    3.78 +
    3.79 +		//Text constructor
    3.80 +		public DataField(int aIndex, string aText = "", ContentAlignment aAlignment = ContentAlignment.MiddleLeft)
    3.81 +		{
    3.82 +			ColumnSpan = 1;
    3.83 +			RowSpan = 1;
    3.84 +			Index = aIndex;
    3.85 +			Text = aText;
    3.86 +			Alignment = aAlignment;
    3.87 +			//
    3.88 +			Bitmap = null;
    3.89 +		}
    3.90 +
    3.91 +		//Bitmap constructor
    3.92 +		public DataField(int aIndex, Bitmap aBitmap)
    3.93 +		{
    3.94 +			ColumnSpan = 1;
    3.95 +			RowSpan = 1;
    3.96 +			Index = aIndex;
    3.97 +			Bitmap = aBitmap;
    3.98 +			//Text
    3.99 +			Text = "";
   3.100 +			Alignment = ContentAlignment.MiddleLeft;
   3.101 +		}
   3.102 +
   3.103 +
   3.104 +		//Generic layout properties
   3.105 +		[DataMember]
   3.106 +		public int Index { get; set; }
   3.107 +
   3.108 +		[DataMember]
   3.109 +		public int Column { get; set; }
   3.110 +
   3.111 +		[DataMember]
   3.112 +		public int Row { get; set; }
   3.113 +
   3.114 +		[DataMember]
   3.115 +		public int ColumnSpan { get; set; }
   3.116 +
   3.117 +		[DataMember]
   3.118 +		public int RowSpan { get; set; }
   3.119 +
   3.120 +		//Text properties
   3.121 +		[DataMember]
   3.122 +		public string Text { get; set; }
   3.123 +
   3.124 +		[DataMember]
   3.125 +		public ContentAlignment Alignment { get; set; }
   3.126 +
   3.127 +		//Bitmap properties
   3.128 +		[DataMember]
   3.129 +		public Bitmap Bitmap { get; set; }
   3.130 +
   3.131 +		//
   3.132 +		public bool IsBitmap { get { return Bitmap != null; } }
   3.133 +		//
   3.134 +		public bool IsText { get { return Bitmap == null; } }
   3.135 +		//
   3.136 +		public bool IsSameLayout(DataField aField)
   3.137 +		{
   3.138 +			return (aField.ColumnSpan == ColumnSpan && aField.RowSpan == RowSpan);
   3.139 +		}
   3.140 +	}
   3.141 +
   3.142 +	/// <summary>
   3.143 +	/// Define our SharpDisplay service.
   3.144 +	/// Clients and servers must implement it to communicate with one another.
   3.145 +	/// Through this service clients can send requests to a server.
   3.146 +	/// Through this service a server session can receive requests from a client.
   3.147 +	/// </summary>
   3.148 +	[ServiceContract(CallbackContract = typeof(ICallback), SessionMode = SessionMode.Required)]
   3.149 +	public interface IService
   3.150 +	{
   3.151 +		/// <summary>
   3.152 +		/// Set the name of this client.
   3.153 +		/// Name is a convenient way to recognize your client.
   3.154 +		/// Naming you client is not mandatory.
   3.155 +		/// In the absence of a name the session ID is often used instead.
   3.156 +		/// </summary>
   3.157 +		/// <param name="aClientName"></param>
   3.158 +		[OperationContract(IsOneWay = true)]
   3.159 +		void SetName(string aClientName);
   3.160 +
   3.161 +		/// <summary>
   3.162 +		/// </summary>
   3.163 +		/// <param name="aLayout"></param>
   3.164 +		[OperationContract(IsOneWay = true)]
   3.165 +		void SetLayout(TableLayout aLayout);
   3.166 +
   3.167 +		/// <summary>
   3.168 +		/// Set the given field on your display.
   3.169 +		/// Fields are often just lines of text or bitmaps.
   3.170 +		/// </summary>
   3.171 +		/// <param name="aTextFieldIndex"></param>
   3.172 +		[OperationContract(IsOneWay = true)]
   3.173 +		void SetField(DataField aField);
   3.174 +
   3.175 +		/// <summary>
   3.176 +		/// Allows a client to set multiple fields at once.
   3.177 +		/// </summary>
   3.178 +		/// <param name="aFields"></param>
   3.179 +		[OperationContract(IsOneWay = true)]
   3.180 +		void SetFields(System.Collections.Generic.IList<DataField> aFields);
   3.181 +
   3.182 +		/// <summary>
   3.183 +		/// Provides the number of clients currently connected
   3.184 +		/// </summary>
   3.185 +		/// <returns></returns>
   3.186 +		[OperationContract()]
   3.187 +		int ClientCount();
   3.188 +
   3.189 +	}
   3.190 +
   3.191 +	/// <summary>
   3.192 +	/// SharDisplay callback provides a means for a server to notify its clients.
   3.193 +	/// </summary>
   3.194 +	public interface ICallback
   3.195 +	{
   3.196 +		[OperationContract(IsOneWay = true)]
   3.197 +		void OnConnected();
   3.198 +
   3.199 +		/// <summary>
   3.200 +		/// Tell our client to close its connection.
   3.201 +		/// Notably sent when the server is shutting down.
   3.202 +		/// </summary>
   3.203 +		[OperationContract(IsOneWay = true)]
   3.204 +		void OnCloseOrder();
   3.205 +	}
   3.206 +
   3.207 +}
     4.1 --- a/OpenHardwareMonitor.csproj	Mon Sep 22 21:59:11 2014 +0200
     4.2 +++ b/OpenHardwareMonitor.csproj	Thu Jan 01 23:35:49 2015 +0100
     4.3 @@ -117,6 +117,7 @@
     4.4      <Compile Include="GUI\SensorSharpDisplay.cs" />
     4.5      <Compile Include="GUI\SharpDisplay.cs" />
     4.6      <Compile Include="GUI\SharpDisplayClient.cs" />
     4.7 +    <Compile Include="GUI\SharpDisplayInterface.cs" />
     4.8      <Compile Include="GUI\ShowDesktop.cs" />
     4.9      <Compile Include="GUI\SoundGraphDisplay.cs" />
    4.10      <Compile Include="GUI\SoundGraphServer.cs" />