Server/MainForm.cs
author Stephane Lenclud
Sun, 24 Jul 2016 13:30:08 +0200
changeset 212 1a0791daa243
parent 211 96f8b4dc4300
child 214 4961ede27e0a
permissions -rw-r--r--
Actions persistence working.
     1 //
     2 // Copyright (C) 2014-2015 Stéphane Lenclud.
     3 //
     4 // This file is part of SharpDisplayManager.
     5 //
     6 // SharpDisplayManager is free software: you can redistribute it and/or modify
     7 // it under the terms of the GNU General Public License as published by
     8 // the Free Software Foundation, either version 3 of the License, or
     9 // (at your option) any later version.
    10 //
    11 // SharpDisplayManager is distributed in the hope that it will be useful,
    12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
    13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14 // GNU General Public License for more details.
    15 //
    16 // You should have received a copy of the GNU General Public License
    17 // along with SharpDisplayManager.  If not, see <http://www.gnu.org/licenses/>.
    18 //
    19 
    20 using System;
    21 using System.Collections.Generic;
    22 using System.ComponentModel;
    23 using System.Data;
    24 using System.Drawing;
    25 using System.Linq;
    26 using System.Text;
    27 using System.Threading.Tasks;
    28 using System.Windows.Forms;
    29 using System.IO;
    30 using CodeProject.Dialog;
    31 using System.Drawing.Imaging;
    32 using System.ServiceModel;
    33 using System.Threading;
    34 using System.Diagnostics;
    35 using System.Deployment.Application;
    36 using System.Reflection;
    37 //NAudio
    38 using NAudio.CoreAudioApi;
    39 using NAudio.CoreAudioApi.Interfaces;
    40 using System.Runtime.InteropServices;
    41 using CecSharp;
    42 //Network
    43 using NETWORKLIST;
    44 //
    45 using SharpDisplayClient;
    46 using SharpDisplay;
    47 using MiniDisplayInterop;
    48 using SharpLib.Display;
    49 using SharpLib.Ear;
    50 
    51 namespace SharpDisplayManager
    52 {
    53     //Types declarations
    54     public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
    55     public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
    56     //Delegates are used for our thread safe method
    57     public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
    58     public delegate void RemoveClientDelegate(string aSessionId);
    59     public delegate void SetFieldDelegate(string SessionId, DataField aField);
    60     public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
    61     public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
    62     public delegate void SetClientNameDelegate(string aSessionId, string aName);
    63     public delegate void SetClientPriorityDelegate(string aSessionId, uint aPriority);
    64     public delegate void PlainUpdateDelegate();
    65     public delegate void WndProcDelegate(ref Message aMessage);
    66 
    67     /// <summary>
    68     /// Our Display manager main form
    69     /// </summary>
    70 	[System.ComponentModel.DesignerCategory("Form")]
    71 	public partial class MainForm : MainFormHid, IMMNotificationClient
    72     {
    73         //public ManagerEventAction iManager = new ManagerEventAction();        
    74         DateTime LastTickTime;
    75         Display iDisplay;
    76         System.Drawing.Bitmap iBmp;
    77         bool iCreateBitmap; //Workaround render to bitmap issues when minimized
    78         ServiceHost iServiceHost;
    79         // Our collection of clients sorted by session id.
    80         public Dictionary<string, ClientData> iClients;
    81         // The name of the client which informations are currently displayed.
    82         public string iCurrentClientSessionId;
    83         ClientData iCurrentClientData;
    84         //
    85         public bool iClosing;
    86 		//
    87 		public bool iSkipFrameRendering;
    88         //Function pointer for pixel color filtering
    89         ColorProcessingDelegate iColorFx;
    90         //Function pointer for pixel X coordinate intercept
    91         CoordinateTranslationDelegate iScreenX;
    92         //Function pointer for pixel Y coordinate intercept
    93         CoordinateTranslationDelegate iScreenY;
    94 		//NAudio
    95 		private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
    96 		private MMDevice iMultiMediaDevice;
    97 		//Network
    98 		private NetworkManager iNetworkManager;
    99 
   100         /// <summary>
   101         /// CEC - Consumer Electronic Control.
   102         /// Notably used to turn TV on and off as Windows broadcast monitor on and off notifications.
   103         /// </summary>
   104         private ConsumerElectronicControl iCecManager;
   105 		
   106 		/// <summary>
   107 		/// Manage run when Windows startup option
   108 		/// </summary>
   109 		private StartupManager iStartupManager;
   110 
   111 		/// <summary>
   112 		/// System notification icon used to hide our application from the task bar.
   113 		/// </summary>
   114 		private SharpLib.Notification.Control iNotifyIcon;
   115 
   116         /// <summary>
   117         /// System recording notification icon.
   118         /// </summary>
   119         private SharpLib.Notification.Control iRecordingNotification;
   120 
   121         /// <summary>
   122         /// 
   123         /// </summary>
   124         RichTextBoxTextWriter iWriter;
   125 
   126 
   127         /// <summary>
   128         /// Allow user to receive window messages;
   129         /// </summary>
   130         public event WndProcDelegate OnWndProc;
   131 
   132         public MainForm()
   133         {
   134             ManagerEventAction.Current = Properties.Settings.Default.Actions;
   135             if (ManagerEventAction.Current == null)
   136             {
   137                 //No actions in our settings yet
   138                 ManagerEventAction.Current = new ManagerEventAction();
   139                 Properties.Settings.Default.Actions = ManagerEventAction.Current;
   140             }
   141             else
   142             {
   143                 //We loaded actions from our settings
   144                 //We need to hook them with corresponding events
   145                 ManagerEventAction.Current.Init();
   146             }
   147             iSkipFrameRendering = false;
   148 			iClosing = false;
   149             iCurrentClientSessionId = "";
   150             iCurrentClientData = null;
   151             LastTickTime = DateTime.Now;
   152 			//Instantiate our display and register for events notifications
   153             iDisplay = new Display();
   154 			iDisplay.OnOpened += OnDisplayOpened;
   155 			iDisplay.OnClosed += OnDisplayClosed;
   156 			//
   157 			iClients = new Dictionary<string, ClientData>();
   158 			iStartupManager = new StartupManager();
   159 			iNotifyIcon = new SharpLib.Notification.Control();
   160             iRecordingNotification = new SharpLib.Notification.Control();
   161 
   162             //Have our designer initialize its controls
   163             InitializeComponent();
   164 
   165             //Redirect console output
   166             iWriter = new RichTextBoxTextWriter(richTextBoxLogs);
   167             Console.SetOut(iWriter);
   168 
   169             //Populate device types
   170             PopulateDeviceTypes();
   171 
   172             //Populate optical drives
   173             PopulateOpticalDrives();
   174 
   175 			//Initial status update 
   176             UpdateStatus();
   177 
   178             //We have a bug when drawing minimized and reusing our bitmap
   179             //Though I could not reproduce it on Windows 10
   180             iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   181             iCreateBitmap = false;
   182 
   183 			//Minimize our window if desired
   184 			if (Properties.Settings.Default.StartMinimized)
   185 			{
   186 				WindowState = FormWindowState.Minimized;
   187 			}
   188 
   189         }
   190 
   191 		/// <summary>
   192 		///
   193 		/// </summary>
   194 		/// <param name="sender"></param>
   195 		/// <param name="e"></param>
   196         private void MainForm_Load(object sender, EventArgs e)
   197         {
   198 			//Check if we are running a Click Once deployed application
   199 			if (ApplicationDeployment.IsNetworkDeployed)
   200 			{
   201 				//This is a proper Click Once installation, fetch and show our version number
   202 				this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
   203 			}
   204 			else
   205 			{
   206 				//Not a proper Click Once installation, assuming development build then
   207 				this.Text += " - development";
   208 			}
   209 
   210 			//NAudio
   211 			iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
   212 			iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);			
   213 			UpdateAudioDeviceAndMasterVolumeThreadSafe();
   214 
   215 			//Network
   216 			iNetworkManager = new NetworkManager();
   217 			iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
   218 			UpdateNetworkStatus();
   219 
   220             //CEC
   221             iCecManager = new ConsumerElectronicControl();
   222             OnWndProc += iCecManager.OnWndProc;
   223             ResetCec();
   224 
   225             //Setup Events
   226             SetupEvents();
   227 
   228             //Setup notification icon
   229             SetupTrayIcon();
   230 
   231             //Setup recording notification
   232             SetupRecordingNotification();
   233 
   234             // To make sure start up with minimize to tray works
   235             if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
   236 			{
   237 				Visible = false;
   238 			}
   239 
   240 #if !DEBUG
   241 			//When not debugging we want the screen to be empty until a client takes over
   242 			ClearLayout();
   243 #else
   244 			//When developing we want at least one client for testing
   245 			StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
   246 #endif
   247 
   248 			//Open display connection on start-up if needed
   249 			if (Properties.Settings.Default.DisplayConnectOnStartup)
   250 			{
   251 				OpenDisplayConnection();
   252 			}
   253 
   254 			//Start our server so that we can get client requests
   255 			StartServer();
   256 
   257 			//Register for HID events
   258 			RegisterHidDevices();
   259 
   260             //Start Idle client if needed
   261             if (Properties.Settings.Default.StartIdleClient)
   262             {
   263                 StartIdleClient();
   264             }
   265         }
   266 
   267 		/// <summary>
   268 		/// Called when our display is opened.
   269 		/// </summary>
   270 		/// <param name="aDisplay"></param>
   271 		private void OnDisplayOpened(Display aDisplay)
   272 		{
   273             //Make sure we resume frame rendering
   274             iSkipFrameRendering = false;
   275 
   276 			//Set our screen size now that our display is connected
   277 			//Our panelDisplay is the container of our tableLayoutPanel
   278 			//tableLayoutPanel will resize itself to fit the client size of our panelDisplay
   279 			//panelDisplay needs an extra 2 pixels for borders on each sides
   280 			//tableLayoutPanel will eventually be the exact size of our display
   281 			Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
   282 			panelDisplay.Size = size;
   283 
   284 			//Our display was just opened, update our UI
   285 			UpdateStatus();
   286 			//Initiate asynchronous request
   287 			iDisplay.RequestFirmwareRevision();
   288 
   289 			//Audio
   290 			UpdateMasterVolumeThreadSafe();
   291 			//Network
   292 			UpdateNetworkStatus();
   293 
   294 #if DEBUG
   295 			//Testing icon in debug, no arm done if icon not supported
   296 			//iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
   297 			//iDisplay.SetAllIconsStatus(2);
   298 #endif
   299 
   300 		}
   301 
   302         /// <summary>
   303         /// 
   304         /// </summary>
   305         private void SetupEvents()
   306         {
   307             //Reset our tree
   308             iTreeViewEvents.Nodes.Clear();
   309             //Populate registered events
   310             foreach (string key in ManagerEventAction.Current.Events.Keys)
   311             {
   312                 Event e = ManagerEventAction.Current.Events[key];
   313                 TreeNode eventNode = iTreeViewEvents.Nodes.Add(key,e.Name);
   314                 eventNode.Tag = e;
   315                 eventNode.Nodes.Add(key + ".Description", e.Description);
   316                 TreeNode actionsNodes = eventNode.Nodes.Add(key + ".Actions", "Actions");
   317 
   318                 foreach (SharpLib.Ear.Action a in e.Actions)
   319                 {
   320                     actionsNodes.Nodes.Add(a.Name);
   321                 }
   322             }
   323 
   324         }
   325 
   326 		/// <summary>
   327 		/// Called when our display is closed.
   328 		/// </summary>
   329 		/// <param name="aDisplay"></param>
   330 		private void OnDisplayClosed(Display aDisplay)
   331 		{
   332             //Our display was just closed, update our UI consequently
   333             UpdateStatus();
   334 		}
   335 
   336 		public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
   337 		{
   338 			//Update network status
   339 			UpdateNetworkStatus();			
   340 		}
   341 
   342 		/// <summary>
   343 		/// Update our Network Status
   344 		/// </summary>
   345 		private void UpdateNetworkStatus()
   346 		{
   347 			if (iDisplay.IsOpen())
   348 			{
   349                 iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
   350                 iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
   351 			}
   352 		}
   353 
   354 
   355 		int iLastNetworkIconIndex = 0;
   356 		int iUpdateCountSinceLastNetworkAnimation = 0;
   357 
   358 		/// <summary>
   359 		/// 
   360 		/// </summary>
   361 		private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
   362 		{
   363 			iUpdateCountSinceLastNetworkAnimation++;
   364 			iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
   365 
   366 			if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
   367 			{
   368                 int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal);
   369 				if (iconCount <= 0)
   370 				{
   371 					//Prevents div by zero and other undefined behavior
   372 					return;
   373 				}
   374 				iLastNetworkIconIndex++;
   375 				iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2);
   376 				for (int i=0;i<iconCount;i++)
   377 				{
   378 					if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && !(i == 1 && iLastNetworkIconIndex > 4))
   379 					{
   380                         iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i);
   381 					}
   382 					else
   383 					{
   384                         iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i);
   385 					}
   386 				}				
   387 			}
   388 		}
   389 
   390 
   391 
   392         /// <summary>
   393         /// Receive volume change notification and reflect changes on our slider.
   394         /// </summary>
   395         /// <param name="data"></param>
   396         public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
   397         {
   398 			UpdateMasterVolumeThreadSafe();
   399         }
   400 
   401         /// <summary>
   402         /// Update master volume when user moves our slider.
   403         /// </summary>
   404         /// <param name="sender"></param>
   405         /// <param name="e"></param>
   406         private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
   407         {
   408 			//Just like Windows Volume Mixer we unmute if the volume is adjusted
   409 			iMultiMediaDevice.AudioEndpointVolume.Mute = false;
   410 			//Set volume level according to our volume slider new position
   411 			iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
   412         }
   413 
   414 
   415 		/// <summary>
   416 		/// Mute check box changed.
   417 		/// </summary>
   418 		/// <param name="sender"></param>
   419 		/// <param name="e"></param>
   420 		private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
   421 		{
   422 			iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
   423 		}
   424 
   425         /// <summary>
   426         /// Device State Changed
   427         /// </summary>
   428         public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
   429 
   430         /// <summary>
   431         /// Device Added
   432         /// </summary>
   433         public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
   434 
   435         /// <summary>
   436         /// Device Removed
   437         /// </summary>
   438         public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
   439 
   440         /// <summary>
   441         /// Default Device Changed
   442         /// </summary>
   443         public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
   444         {
   445             if (role == Role.Multimedia && flow == DataFlow.Render)
   446             {
   447                 UpdateAudioDeviceAndMasterVolumeThreadSafe();
   448             }
   449         }
   450 
   451         /// <summary>
   452         /// Property Value Changed
   453         /// </summary>
   454         /// <param name="pwstrDeviceId"></param>
   455         /// <param name="key"></param>
   456         public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
   457 
   458 
   459         
   460 
   461 		/// <summary>
   462 		/// Update master volume indicators based our current system states.
   463 		/// This typically includes volume levels and mute status.
   464 		/// </summary>
   465 		private void UpdateMasterVolumeThreadSafe()
   466 		{
   467 			if (this.InvokeRequired)
   468 			{
   469 				//Not in the proper thread, invoke ourselves
   470 				PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
   471 				this.Invoke(d, new object[] { });
   472 				return;
   473 			}
   474 
   475 			//Update volume slider
   476 			float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
   477 			trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
   478 			//Update mute checkbox
   479 			checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
   480 
   481 			//If our display connection is open we need to update its icons
   482 			if (iDisplay.IsOpen())
   483 			{
   484 				//First take care our our volume level icons
   485                 int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume);
   486 				if (volumeIconCount > 0)
   487 				{					
   488 					//Compute current volume level from system level and the number of segments in our display volume bar.
   489 					//That tells us how many segments in our volume bar needs to be turned on.
   490 					float currentVolume = volumeLevelScalar * volumeIconCount;
   491 					int segmentOnCount = Convert.ToInt32(currentVolume);
   492 					//Check if our segment count was rounded up, this will later be used for half brightness segment
   493 					bool roundedUp = segmentOnCount > currentVolume;
   494 
   495 					for (int i = 0; i < volumeIconCount; i++)
   496 					{
   497 						if (i < segmentOnCount)
   498 						{
   499 							//If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
   500 							if (i == segmentOnCount - 1 && roundedUp)
   501 							{
   502 								//Half brightness
   503                                 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1) / 2);
   504 							}
   505 							else
   506 							{
   507 								//Full brightness
   508                                 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1);
   509 							}
   510 						}
   511 						else
   512 						{
   513                             iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0);
   514 						}
   515 					}
   516 				}
   517 
   518 				//Take care of our mute icon
   519                 iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iMultiMediaDevice.AudioEndpointVolume.Mute);
   520 			}
   521 
   522 		}
   523 
   524         /// <summary>
   525         /// 
   526         /// </summary>
   527         private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
   528         {
   529             if (this.InvokeRequired)
   530             {
   531                 //Not in the proper thread, invoke ourselves
   532 				PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
   533                 this.Invoke(d, new object[] { });
   534                 return;
   535             }
   536             
   537             //We are in the correct thread just go ahead.
   538             try
   539             {                
   540                 //Get our master volume            
   541 				iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
   542 				//Update our label
   543 				labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
   544 
   545                 //Show our volume in our track bar
   546 				UpdateMasterVolumeThreadSafe();
   547 
   548                 //Register to get volume modifications
   549 				iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
   550                 //
   551 				trackBarMasterVolume.Enabled = true;
   552             }
   553             catch (Exception ex)
   554             {
   555                 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
   556                 Debug.WriteLine(ex.ToString());
   557                 //Something went wrong S/PDIF device ca throw exception I guess
   558 				trackBarMasterVolume.Enabled = false;
   559             }
   560         }
   561 
   562 		/// <summary>
   563 		/// 
   564 		/// </summary>
   565 		private void PopulateDeviceTypes()
   566 		{
   567 			int count = Display.TypeCount();
   568 
   569 			for (int i = 0; i < count; i++)
   570 			{
   571 				comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type)i));
   572 			}
   573 		}
   574 
   575         /// <summary>
   576         /// 
   577         /// </summary>
   578         private void PopulateOpticalDrives()
   579         {
   580             //Reset our list of drives
   581             comboBoxOpticalDrives.Items.Clear();
   582             comboBoxOpticalDrives.Items.Add("None");
   583 
   584             //Go through each drives on our system and collected the optical ones in our list
   585             DriveInfo[] allDrives = DriveInfo.GetDrives();
   586             foreach (DriveInfo d in allDrives)
   587             {
   588                 Debug.WriteLine("Drive " + d.Name);
   589                 Debug.WriteLine("  Drive type: {0}", d.DriveType);
   590 
   591                 if (d.DriveType==DriveType.CDRom)
   592                 {
   593                     //This is an optical drive, add it now
   594                     comboBoxOpticalDrives.Items.Add(d.Name.Substring(0,2));
   595                 }                
   596             }           
   597         }
   598 
   599         /// <summary>
   600         /// 
   601         /// </summary>
   602         /// <returns></returns>
   603         public string OpticalDriveToEject()
   604         {
   605             return comboBoxOpticalDrives.SelectedItem.ToString();
   606         }
   607 
   608 
   609 
   610 		/// <summary>
   611 		///
   612 		/// </summary>
   613 		private void SetupTrayIcon()
   614 		{
   615 			iNotifyIcon.Icon = GetIcon("vfd.ico");
   616 			iNotifyIcon.Text = "Sharp Display Manager";
   617 			iNotifyIcon.Visible = true;
   618 
   619 			//Double click toggles visibility - typically brings up the application
   620 			iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
   621 			{
   622 				SysTrayHideShow();
   623 			};
   624 
   625 			//Adding a context menu, useful to be able to exit the application
   626 			ContextMenu contextMenu = new ContextMenu();
   627 			//Context menu item to toggle visibility
   628 			MenuItem hideShowItem = new MenuItem("Hide/Show");
   629 			hideShowItem.Click += delegate(object obj, EventArgs args)
   630 			{
   631 				SysTrayHideShow();
   632 			};
   633 			contextMenu.MenuItems.Add(hideShowItem);
   634 
   635 			//Context menu item separator
   636 			contextMenu.MenuItems.Add(new MenuItem("-"));
   637 
   638 			//Context menu exit item
   639 			MenuItem exitItem = new MenuItem("Exit");
   640 			exitItem.Click += delegate(object obj, EventArgs args)
   641 			{
   642 				Application.Exit();
   643 			};
   644 			contextMenu.MenuItems.Add(exitItem);
   645 
   646 			iNotifyIcon.ContextMenu = contextMenu;
   647 		}
   648 
   649         /// <summary>
   650         ///
   651         /// </summary>
   652         private void SetupRecordingNotification()
   653         {
   654             iRecordingNotification.Icon = GetIcon("record.ico");
   655             iRecordingNotification.Text = "No recording";
   656             iRecordingNotification.Visible = false;
   657         }
   658 
   659         /// <summary>
   660         /// Access icons from embedded resources.
   661         /// </summary>
   662         /// <param name="aName"></param>
   663         /// <returns></returns>
   664         public static Icon GetIcon(string aName)
   665 		{
   666 			string[] names =  Assembly.GetExecutingAssembly().GetManifestResourceNames();
   667 			foreach (string name in names)
   668 			{
   669                 //Find a resource name that ends with the given name
   670 				if (name.EndsWith(aName))
   671 				{
   672 					using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
   673 					{
   674 						return new Icon(stream);
   675 					}
   676 				}
   677 			}
   678 
   679 			return null;
   680 		}
   681 
   682 
   683         /// <summary>
   684         /// Set our current client.
   685         /// This will take care of applying our client layout and set data fields.
   686         /// </summary>
   687         /// <param name="aSessionId"></param>
   688         void SetCurrentClient(string aSessionId, bool aForce=false)
   689         {
   690             if (aSessionId == iCurrentClientSessionId)
   691             {
   692                 //Given client is already the current one.
   693                 //Don't bother changing anything then.
   694                 return;
   695             }
   696 
   697             ClientData requestedClientData = iClients[aSessionId];
   698 
   699             //Check when was the last time we switched to that client
   700             if (iCurrentClientData != null)
   701             {
   702                 //Do not switch client if priority of current client is higher 
   703                 if (!aForce && requestedClientData.Priority < iCurrentClientData.Priority)
   704                 {
   705                     return;
   706                 }
   707 
   708 
   709                 double lastSwitchToClientSecondsAgo = (DateTime.Now - iCurrentClientData.LastSwitchTime).TotalSeconds;
   710                 //TODO: put that hard coded value as a client property
   711                 //Clients should be able to define how often they can be interrupted
   712                 //Thus a background client can set this to zero allowing any other client to interrupt at any time
   713                 //We could also compute this delay by looking at the requests frequencies?
   714                 if (!aForce &&
   715                     requestedClientData.Priority == iCurrentClientData.Priority && //Time sharing is only if clients have the same priority
   716                     (lastSwitchToClientSecondsAgo < 30)) //Make sure a client is on for at least 30 seconds
   717                 {
   718                     //Don't switch clients too often
   719                     return;
   720                 }
   721             }
   722 
   723             //Set current client ID.
   724             iCurrentClientSessionId = aSessionId;
   725             //Set the time we last switched to that client
   726             iClients[aSessionId].LastSwitchTime = DateTime.Now;
   727             //Fetch and set current client data.
   728             iCurrentClientData = requestedClientData;
   729             //Apply layout and set data fields.
   730             UpdateTableLayoutPanel(iCurrentClientData);
   731         }
   732 
   733         private void buttonFont_Click(object sender, EventArgs e)
   734         {
   735             //fontDialog.ShowColor = true;
   736             //fontDialog.ShowApply = true;
   737             fontDialog.ShowEffects = true;
   738             fontDialog.Font = cds.Font;
   739 
   740             fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
   741 
   742             //fontDialog.ShowHelp = true;
   743 
   744             //fontDlg.MaxSize = 40;
   745             //fontDlg.MinSize = 22;
   746 
   747             //fontDialog.Parent = this;
   748             //fontDialog.StartPosition = FormStartPosition.CenterParent;
   749 
   750             //DlgBox.ShowDialog(fontDialog);
   751 
   752             //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
   753             if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
   754             {
   755                 //Set the fonts to all our labels in our layout
   756                 foreach (Control ctrl in iTableLayoutPanel.Controls)
   757                 {
   758                     if (ctrl is MarqueeLabel)
   759                     {
   760                         ((MarqueeLabel)ctrl).Font = fontDialog.Font;
   761                     }
   762                 }
   763 
   764                 //Save font settings
   765                 cds.Font = fontDialog.Font;
   766                 Properties.Settings.Default.Save();
   767                 //
   768                 CheckFontHeight();
   769             }
   770         }
   771 
   772         /// <summary>
   773         ///
   774         /// </summary>
   775         void CheckFontHeight()
   776         {
   777             //Show font height and width
   778             labelFontHeight.Text = "Font height: " + cds.Font.Height;
   779             float charWidth = IsFixedWidth(cds.Font);
   780             if (charWidth == 0.0f)
   781             {
   782                 labelFontWidth.Visible = false;
   783             }
   784             else
   785             {
   786                 labelFontWidth.Visible = true;
   787                 labelFontWidth.Text = "Font width: " + charWidth;
   788             }
   789 
   790             MarqueeLabel label = null;
   791             //Get the first label control we can find
   792             foreach (Control ctrl in iTableLayoutPanel.Controls)
   793             {
   794                 if (ctrl is MarqueeLabel)
   795                 {
   796                     label = (MarqueeLabel)ctrl;
   797                     break;
   798                 }
   799             }
   800 
   801             //Now check font height and show a warning if needed.
   802             if (label != null && label.Font.Height > label.Height)
   803             {
   804                 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
   805                 labelWarning.Visible = true;
   806             }
   807             else
   808             {
   809                 labelWarning.Visible = false;
   810             }
   811 
   812         }
   813 
   814         private void buttonCapture_Click(object sender, EventArgs e)
   815         {
   816             System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height);
   817             iTableLayoutPanel.DrawToBitmap(bmp, iTableLayoutPanel.ClientRectangle);
   818             //Bitmap bmpToSave = new Bitmap(bmp);
   819             bmp.Save("D:\\capture.png");
   820 
   821             ((MarqueeLabel)iTableLayoutPanel.Controls[0]).Text = "Captured";
   822 
   823             /*
   824             string outputFileName = "d:\\capture.png";
   825             using (MemoryStream memory = new MemoryStream())
   826             {
   827                 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
   828                 {
   829                     bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
   830                     byte[] bytes = memory.ToArray();
   831                     fs.Write(bytes, 0, bytes.Length);
   832                 }
   833             }
   834              */
   835 
   836         }
   837 
   838         private void CheckForRequestResults()
   839         {
   840             if (iDisplay.IsRequestPending())
   841             {
   842                 switch (iDisplay.AttemptRequestCompletion())
   843                 {
   844                     case MiniDisplay.Request.FirmwareRevision:
   845                         toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
   846                         //Issue next request then
   847                         iDisplay.RequestPowerSupplyStatus();
   848                         break;
   849 
   850                     case MiniDisplay.Request.PowerSupplyStatus:
   851                         if (iDisplay.PowerSupplyStatus())
   852                         {
   853                             toolStripStatusLabelPower.Text = "ON";
   854                         }
   855                         else
   856                         {
   857                             toolStripStatusLabelPower.Text = "OFF";
   858                         }
   859                         //Issue next request then
   860                         iDisplay.RequestDeviceId();
   861                         break;
   862 
   863                     case MiniDisplay.Request.DeviceId:
   864                         toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
   865                         //No more request to issue
   866                         break;
   867                 }
   868             }
   869         }
   870 
   871         public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
   872         {
   873             if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
   874             {
   875                 return 0xFFFFFFFF;
   876             }
   877             return 0x00000000;
   878         }
   879 
   880         public static uint ColorUntouched(int aX, int aY, uint aPixel)
   881         {
   882             return aPixel;
   883         }
   884 
   885         public static uint ColorInversed(int aX, int aY, uint aPixel)
   886         {
   887             return ~aPixel;
   888         }
   889 
   890         public static uint ColorChessboard(int aX, int aY, uint aPixel)
   891         {
   892             if ((aX % 2 == 0) && (aY % 2 == 0))
   893             {
   894                 return ~aPixel;
   895             }
   896             else if ((aX % 2 != 0) && (aY % 2 != 0))
   897             {
   898                 return ~aPixel;
   899             }
   900             return 0x00000000;
   901         }
   902 
   903 
   904         public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
   905         {
   906             return aBmp.Width - aX - 1;
   907         }
   908 
   909         public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
   910         {
   911             return iBmp.Height - aY - 1;
   912         }
   913 
   914         public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
   915         {
   916             return aX;
   917         }
   918 
   919         public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
   920         {
   921             return aY;
   922         }
   923 
   924         /// <summary>
   925         /// Select proper pixel delegates according to our current settings.
   926         /// </summary>
   927         private void SetupPixelDelegates()
   928         {
   929             //Select our pixel processing routine
   930             if (cds.InverseColors)
   931             {
   932                 //iColorFx = ColorChessboard;
   933                 iColorFx = ColorInversed;
   934             }
   935             else
   936             {
   937                 iColorFx = ColorWhiteIsOn;
   938             }
   939 
   940             //Select proper coordinate translation functions
   941             //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
   942             if (cds.ReverseScreen)
   943             {
   944                 iScreenX = ScreenReversedX;
   945                 iScreenY = ScreenReversedY;
   946             }
   947             else
   948             {
   949                 iScreenX = ScreenX;
   950                 iScreenY = ScreenY;
   951             }
   952 
   953         }
   954 
   955         //This is our timer tick responsible to perform our render
   956         private void timer_Tick(object sender, EventArgs e)
   957         {
   958             //Not ideal cause this has nothing to do with display render
   959             LogsUpdate();
   960 
   961             //Update our animations
   962             DateTime NewTickTime = DateTime.Now;
   963 
   964 			UpdateNetworkSignal(LastTickTime, NewTickTime);
   965 
   966             //Update animation for all our marquees
   967             foreach (Control ctrl in iTableLayoutPanel.Controls)
   968             {
   969                 if (ctrl is MarqueeLabel)
   970                 {
   971                     ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
   972                 }
   973             }
   974 
   975             //Update our display
   976             if (iDisplay.IsOpen())
   977             {
   978                 CheckForRequestResults();
   979 
   980 				//Check if frame rendering is needed
   981 				//Typically used when showing clock
   982 				if (!iSkipFrameRendering)
   983 				{
   984 					//Draw to bitmap
   985 					if (iCreateBitmap)
   986 					{
   987                         iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   988                         iCreateBitmap = false;
   989                     }
   990 					iTableLayoutPanel.DrawToBitmap(iBmp, iTableLayoutPanel.ClientRectangle);
   991 					//iBmp.Save("D:\\capture.png");
   992 
   993 					//Send it to our display
   994 					for (int i = 0; i < iBmp.Width; i++)
   995 					{
   996 						for (int j = 0; j < iBmp.Height; j++)
   997 						{
   998 							unchecked
   999 							{
  1000 								//Get our processed pixel coordinates
  1001 								int x = iScreenX(iBmp, i);
  1002 								int y = iScreenY(iBmp, j);
  1003 								//Get pixel color
  1004 								uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
  1005 								//Apply color effects
  1006 								color = iColorFx(x, y, color);
  1007 								//Now set our pixel
  1008 								iDisplay.SetPixel(x, y, color);
  1009 							}
  1010 						}
  1011 					}
  1012 
  1013 					iDisplay.SwapBuffers();
  1014 				}
  1015             }
  1016 
  1017             //Compute instant FPS
  1018             toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
  1019 
  1020             LastTickTime = NewTickTime;
  1021 
  1022         }
  1023 
  1024 		/// <summary>
  1025 		/// Attempt to establish connection with our display hardware.
  1026 		/// </summary>
  1027         private void OpenDisplayConnection()
  1028         {
  1029             CloseDisplayConnection();
  1030 
  1031             if (!iDisplay.Open((MiniDisplay.Type)cds.DisplayType))
  1032             {   
  1033 				UpdateStatus();               
  1034 				toolStripStatusLabelConnect.Text = "Connection error";
  1035             }
  1036         }
  1037 
  1038         private void CloseDisplayConnection()
  1039         {
  1040 			//Status will be updated upon receiving the closed event
  1041 
  1042 			if (iDisplay == null || !iDisplay.IsOpen())
  1043 			{
  1044 				return;
  1045 			}
  1046 
  1047 			//Do not clear if we gave up on rendering already.
  1048 			//This means we will keep on displaying clock on MDM166AA for instance.
  1049 			if (!iSkipFrameRendering)
  1050 			{
  1051 				iDisplay.Clear();
  1052 				iDisplay.SwapBuffers();
  1053 			}
  1054 
  1055 			iDisplay.SetAllIconsStatus(0); //Turn off all icons
  1056             iDisplay.Close();
  1057         }
  1058 
  1059         private void buttonOpen_Click(object sender, EventArgs e)
  1060         {
  1061             OpenDisplayConnection();
  1062         }
  1063 
  1064         private void buttonClose_Click(object sender, EventArgs e)
  1065         {
  1066             CloseDisplayConnection();
  1067         }
  1068 
  1069         private void buttonClear_Click(object sender, EventArgs e)
  1070         {
  1071             iDisplay.Clear();
  1072             iDisplay.SwapBuffers();
  1073         }
  1074 
  1075         private void buttonFill_Click(object sender, EventArgs e)
  1076         {
  1077             iDisplay.Fill();
  1078             iDisplay.SwapBuffers();
  1079         }
  1080 
  1081         private void trackBarBrightness_Scroll(object sender, EventArgs e)
  1082         {
  1083             cds.Brightness = trackBarBrightness.Value;
  1084             Properties.Settings.Default.Save();
  1085             iDisplay.SetBrightness(trackBarBrightness.Value);
  1086 
  1087         }
  1088 
  1089 
  1090         /// <summary>
  1091         /// CDS stands for Current Display Settings
  1092         /// </summary>
  1093         private DisplaySettings cds
  1094         {
  1095             get
  1096             {
  1097                 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
  1098                 if (settings == null)
  1099                 {
  1100                     settings = new DisplaysSettings();
  1101                     settings.Init();
  1102                     Properties.Settings.Default.DisplaysSettings = settings;
  1103                 }
  1104 
  1105                 //Make sure all our settings have been created
  1106                 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
  1107                 {
  1108                     settings.Displays.Add(new DisplaySettings());
  1109                 }
  1110 
  1111                 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
  1112                 return displaySettings;
  1113             }
  1114         }
  1115 
  1116         /// <summary>
  1117         /// Check if the given font has a fixed character pitch.
  1118         /// </summary>
  1119         /// <param name="ft"></param>
  1120         /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
  1121         public float IsFixedWidth(Font ft)
  1122         {
  1123             Graphics g = CreateGraphics();
  1124             char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
  1125             float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
  1126 
  1127             bool fixedWidth = true;
  1128 
  1129             foreach (char c in charSizes)
  1130                 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
  1131                     fixedWidth = false;
  1132 
  1133             if (fixedWidth)
  1134             {
  1135                 return charWidth;
  1136             }
  1137 
  1138             return 0.0f;
  1139         }
  1140 
  1141 		/// <summary>
  1142 		/// Synchronize UI with settings
  1143 		/// </summary>
  1144         private void UpdateStatus()
  1145         {            
  1146             //Load settings
  1147             checkBoxShowBorders.Checked = cds.ShowBorders;
  1148             iTableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
  1149 
  1150             //Set the proper font to each of our labels
  1151             foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls)
  1152             {
  1153                 ctrl.Font = cds.Font;
  1154             }
  1155 
  1156             CheckFontHeight();
  1157 			//Check if "run on Windows startup" is enabled
  1158 			checkBoxAutoStart.Checked = iStartupManager.Startup;
  1159 			//
  1160             checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
  1161 			checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
  1162 			checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
  1163             iCheckBoxStartIdleClient.Checked = Properties.Settings.Default.StartIdleClient;
  1164             labelStartFileName.Text = Properties.Settings.Default.StartFileName;
  1165 
  1166 
  1167             //Try find our drive in our drive list
  1168             int opticalDriveItemIndex=0;
  1169             bool driveNotFound = true;
  1170             string opticalDriveToEject=Properties.Settings.Default.OpticalDriveToEject;
  1171             foreach (object item in comboBoxOpticalDrives.Items)
  1172             {
  1173                 if (opticalDriveToEject == item.ToString())
  1174                 {
  1175                     comboBoxOpticalDrives.SelectedIndex = opticalDriveItemIndex;
  1176                     driveNotFound = false;
  1177                     break;
  1178                 }
  1179                 opticalDriveItemIndex++;
  1180             }
  1181 
  1182             if (driveNotFound)
  1183             {
  1184                 //We could not find the drive we had saved.
  1185                 //Select "None" then.
  1186                 comboBoxOpticalDrives.SelectedIndex = 0;
  1187             }
  1188 
  1189             //CEC settings
  1190             checkBoxCecEnabled.Checked = Properties.Settings.Default.CecEnabled;
  1191             checkBoxCecMonitorOn.Checked = Properties.Settings.Default.CecMonitorOn;
  1192             checkBoxCecMonitorOff.Checked = Properties.Settings.Default.CecMonitorOff;
  1193 		    checkBoxCecReconnectToPowerTv.Checked = Properties.Settings.Default.CecReconnectToPowerTv;
  1194             comboBoxHdmiPort.SelectedIndex = Properties.Settings.Default.CecHdmiPort - 1;
  1195 
  1196             //Mini Display settings
  1197             checkBoxReverseScreen.Checked = cds.ReverseScreen;
  1198             checkBoxInverseColors.Checked = cds.InverseColors;
  1199 			checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
  1200             checkBoxScaleToFit.Checked = cds.ScaleToFit;
  1201             maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
  1202             labelMinFontSize.Enabled = cds.ScaleToFit;
  1203             maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
  1204 			maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
  1205             comboBoxDisplayType.SelectedIndex = cds.DisplayType;
  1206             timer.Interval = cds.TimerInterval;
  1207             maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
  1208             textBoxScrollLoopSeparator.Text = cds.Separator;
  1209             //
  1210             SetupPixelDelegates();
  1211 
  1212             if (iDisplay.IsOpen())
  1213             {
  1214                 //We have a display connection
  1215                 //Reflect that in our UI
  1216                 StartTimer();
  1217 
  1218 				iTableLayoutPanel.Enabled = true;
  1219 				panelDisplay.Enabled = true;
  1220 
  1221                 //Only setup brightness if display is open
  1222                 trackBarBrightness.Minimum = iDisplay.MinBrightness();
  1223                 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
  1224 				if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
  1225 				{
  1226 					//Brightness out of range, this can occur when using auto-detect
  1227 					//Use max brightness instead
  1228 					trackBarBrightness.Value = iDisplay.MaxBrightness();
  1229 					iDisplay.SetBrightness(iDisplay.MaxBrightness());
  1230 				}
  1231 				else
  1232 				{
  1233 					trackBarBrightness.Value = cds.Brightness;
  1234 					iDisplay.SetBrightness(cds.Brightness);
  1235 				}
  1236 
  1237 				//Try compute the steps to something that makes sense
  1238                 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
  1239                 trackBarBrightness.SmallChange = 1;
  1240                 
  1241                 //
  1242                 buttonFill.Enabled = true;
  1243                 buttonClear.Enabled = true;
  1244                 buttonOpen.Enabled = false;
  1245                 buttonClose.Enabled = true;
  1246                 trackBarBrightness.Enabled = true;
  1247                 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
  1248                 //+ " - " + iDisplay.SerialNumber();
  1249 
  1250                 if (iDisplay.SupportPowerOnOff())
  1251                 {
  1252                     buttonPowerOn.Enabled = true;
  1253                     buttonPowerOff.Enabled = true;
  1254                 }
  1255                 else
  1256                 {
  1257                     buttonPowerOn.Enabled = false;
  1258                     buttonPowerOff.Enabled = false;
  1259                 }
  1260 
  1261                 if (iDisplay.SupportClock())
  1262                 {
  1263                     buttonShowClock.Enabled = true;
  1264                     buttonHideClock.Enabled = true;
  1265                 }
  1266                 else
  1267                 {
  1268                     buttonShowClock.Enabled = false;
  1269                     buttonHideClock.Enabled = false;
  1270                 }
  1271 
  1272 				
  1273 				//Check if Volume Label is supported. To date only MDM166AA supports that crap :)
  1274 				checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel)>0;
  1275 
  1276 				if (cds.ShowVolumeLabel)
  1277 				{
  1278                     iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
  1279 				}
  1280 				else
  1281 				{
  1282                     iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
  1283 				}
  1284             }
  1285             else
  1286             {
  1287                 //Display connection not available
  1288                 //Reflect that in our UI
  1289 #if DEBUG
  1290                 //In debug start our timer even if we don't have a display connection
  1291                 StartTimer();
  1292 #else
  1293                 //In production environment we don't need our timer if no display connection
  1294                 StopTimer();
  1295 #endif
  1296                 checkBoxShowVolumeLabel.Enabled = false;
  1297 				iTableLayoutPanel.Enabled = false;
  1298 				panelDisplay.Enabled = false;
  1299                 buttonFill.Enabled = false;
  1300                 buttonClear.Enabled = false;
  1301                 buttonOpen.Enabled = true;
  1302                 buttonClose.Enabled = false;
  1303                 trackBarBrightness.Enabled = false;
  1304                 buttonPowerOn.Enabled = false;
  1305                 buttonPowerOff.Enabled = false;
  1306                 buttonShowClock.Enabled = false;
  1307                 buttonHideClock.Enabled = false;
  1308                 toolStripStatusLabelConnect.Text = "Disconnected";
  1309                 toolStripStatusLabelPower.Text = "N/A";
  1310             }
  1311 
  1312         }
  1313 
  1314 
  1315 		/// <summary>
  1316 		/// 
  1317 		/// </summary>
  1318 		/// <param name="sender"></param>
  1319 		/// <param name="e"></param>
  1320 		private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
  1321 		{
  1322 			cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
  1323 			Properties.Settings.Default.Save();
  1324 			UpdateStatus();
  1325 		}
  1326 
  1327         private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
  1328         {
  1329             //Save our show borders setting
  1330             iTableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
  1331             cds.ShowBorders = checkBoxShowBorders.Checked;
  1332             Properties.Settings.Default.Save();
  1333             CheckFontHeight();
  1334         }
  1335 
  1336         private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
  1337         {
  1338             //Save our connect on startup setting
  1339             Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
  1340             Properties.Settings.Default.Save();
  1341         }
  1342 
  1343 		private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
  1344 		{
  1345 			//Save our "Minimize to tray" setting
  1346 			Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
  1347 			Properties.Settings.Default.Save();
  1348 
  1349 		}
  1350 
  1351 		private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
  1352 		{
  1353 			//Save our "Start minimized" setting
  1354 			Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
  1355 			Properties.Settings.Default.Save();
  1356 		}
  1357 
  1358         private void checkBoxStartIdleClient_CheckedChanged(object sender, EventArgs e)
  1359         {
  1360             Properties.Settings.Default.StartIdleClient = iCheckBoxStartIdleClient.Checked;
  1361             Properties.Settings.Default.Save();
  1362         }
  1363 
  1364         private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
  1365 		{
  1366 			iStartupManager.Startup = checkBoxAutoStart.Checked;
  1367 		}
  1368 
  1369 
  1370         private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
  1371         {
  1372             //Save our reverse screen setting
  1373             cds.ReverseScreen = checkBoxReverseScreen.Checked;
  1374             Properties.Settings.Default.Save();
  1375             SetupPixelDelegates();
  1376         }
  1377 
  1378         private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
  1379         {
  1380             //Save our inverse colors setting
  1381             cds.InverseColors = checkBoxInverseColors.Checked;
  1382             Properties.Settings.Default.Save();
  1383             SetupPixelDelegates();
  1384         }
  1385 
  1386         private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
  1387         {
  1388             //Save our scale to fit setting
  1389             cds.ScaleToFit = checkBoxScaleToFit.Checked;
  1390             Properties.Settings.Default.Save();
  1391             //
  1392             labelMinFontSize.Enabled = cds.ScaleToFit;
  1393             maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
  1394         }
  1395 
  1396         private void MainForm_Resize(object sender, EventArgs e)
  1397         {
  1398             if (WindowState == FormWindowState.Minimized)
  1399             {
  1400                 // To workaround our empty bitmap bug on Windows 7 we need to recreate our bitmap when the application is minimized
  1401                 // That's apparently not needed on Windows 10 but we better leave it in place.
  1402                 iCreateBitmap = true;
  1403             }
  1404         }
  1405 
  1406         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
  1407         {
  1408             iCecManager.Stop();
  1409 			iNetworkManager.Dispose();
  1410 			CloseDisplayConnection();
  1411             StopServer();
  1412             e.Cancel = iClosing;
  1413         }
  1414 
  1415         public void StartServer()
  1416         {
  1417             iServiceHost = new ServiceHost
  1418                 (
  1419                     typeof(Session),
  1420                     new Uri[] { new Uri("net.tcp://localhost:8001/") }
  1421                 );
  1422 
  1423             iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
  1424             iServiceHost.Open();
  1425         }
  1426 
  1427         public void StopServer()
  1428         {
  1429             if (iClients.Count > 0 && !iClosing)
  1430             {
  1431                 //Tell our clients
  1432                 iClosing = true;
  1433                 BroadcastCloseEvent();
  1434             }
  1435             else if (iClosing)
  1436             {
  1437                 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
  1438                 {
  1439                     iClosing = false; //We make sure we force close if asked twice
  1440                 }
  1441             }
  1442             else
  1443             {
  1444                 //We removed that as it often lags for some reason
  1445                 //iServiceHost.Close();
  1446             }
  1447         }
  1448 
  1449         public void BroadcastCloseEvent()
  1450         {
  1451             Trace.TraceInformation("BroadcastCloseEvent - start");
  1452 
  1453             var inactiveClients = new List<string>();
  1454             foreach (var client in iClients)
  1455             {
  1456                 //if (client.Key != eventData.ClientName)
  1457                 {
  1458                     try
  1459                     {
  1460                         Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
  1461                         client.Value.Callback.OnCloseOrder(/*eventData*/);
  1462                     }
  1463                     catch (Exception ex)
  1464                     {
  1465                         inactiveClients.Add(client.Key);
  1466                     }
  1467                 }
  1468             }
  1469 
  1470             if (inactiveClients.Count > 0)
  1471             {
  1472                 foreach (var client in inactiveClients)
  1473                 {
  1474                     iClients.Remove(client);
  1475                     Program.iMainForm.iTreeViewClients.Nodes.Remove(Program.iMainForm.iTreeViewClients.Nodes.Find(client, false)[0]);
  1476                 }
  1477             }
  1478 
  1479 			if (iClients.Count==0)
  1480 			{
  1481 				ClearLayout();
  1482 			}
  1483         }
  1484 
  1485 		/// <summary>
  1486 		/// Just remove all our fields.
  1487 		/// </summary>
  1488 		private void ClearLayout()
  1489 		{
  1490 			iTableLayoutPanel.Controls.Clear();
  1491 			iTableLayoutPanel.RowStyles.Clear();
  1492 			iTableLayoutPanel.ColumnStyles.Clear();
  1493 			iCurrentClientData = null;
  1494 		}
  1495 
  1496 		/// <summary>
  1497 		/// Just launch a demo client.
  1498 		/// </summary>
  1499 		private void StartNewClient(string aTopText = "", string aBottomText = "")
  1500 		{
  1501 			Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
  1502 			SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
  1503 			clientThread.Start(myParams);
  1504 			BringToFront();
  1505 		}
  1506 
  1507         /// <summary>
  1508         /// Just launch our idle client.
  1509         /// </summary>
  1510         private void StartIdleClient(string aTopText = "", string aBottomText = "")
  1511         {
  1512             Thread clientThread = new Thread(SharpDisplayIdleClient.Program.MainWithParams);
  1513             SharpDisplayIdleClient.StartParams myParams = new SharpDisplayIdleClient.StartParams(new Point(this.Right, this.Top), aTopText, aBottomText);
  1514             clientThread.Start(myParams);
  1515             BringToFront();
  1516         }
  1517 
  1518 
  1519         private void buttonStartClient_Click(object sender, EventArgs e)
  1520         {
  1521 			StartNewClient();
  1522         }
  1523 
  1524         private void buttonSuspend_Click(object sender, EventArgs e)
  1525         {
  1526             ToggleTimer();
  1527         }
  1528 
  1529         private void StartTimer()
  1530         {
  1531             LastTickTime = DateTime.Now; //Reset timer to prevent jump
  1532             timer.Enabled = true;
  1533             UpdateSuspendButton();
  1534         }
  1535 
  1536         private void StopTimer()
  1537         {
  1538             LastTickTime = DateTime.Now; //Reset timer to prevent jump
  1539             timer.Enabled = false;
  1540             UpdateSuspendButton();
  1541         }
  1542 
  1543         private void ToggleTimer()
  1544         {
  1545             LastTickTime = DateTime.Now; //Reset timer to prevent jump
  1546             timer.Enabled = !timer.Enabled;
  1547             UpdateSuspendButton();
  1548         }
  1549 
  1550         private void UpdateSuspendButton()
  1551         {
  1552             if (!timer.Enabled)
  1553             {
  1554                 buttonSuspend.Text = "Run";
  1555             }
  1556             else
  1557             {
  1558                 buttonSuspend.Text = "Pause";
  1559             }
  1560         }
  1561 
  1562 
  1563         private void buttonCloseClients_Click(object sender, EventArgs e)
  1564         {
  1565             BroadcastCloseEvent();
  1566         }
  1567 
  1568         private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
  1569         {
  1570             //Root node must have at least one child
  1571             if (e.Node.Nodes.Count == 0)
  1572             {
  1573                 return;
  1574             }
  1575 
  1576             //If the selected node is the root node of a client then switch to it
  1577             string sessionId=e.Node.Nodes[0].Text; //First child of a root node is the sessionId
  1578             if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at
  1579             {
  1580                 //We have a valid session just switch to that client
  1581                 SetCurrentClient(sessionId,true);
  1582             }
  1583             
  1584         }
  1585 
  1586 
  1587         /// <summary>
  1588         ///
  1589         /// </summary>
  1590         /// <param name="aSessionId"></param>
  1591         /// <param name="aCallback"></param>
  1592         public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
  1593         {
  1594             if (this.InvokeRequired)
  1595             {
  1596                 //Not in the proper thread, invoke ourselves
  1597                 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
  1598                 this.Invoke(d, new object[] { aSessionId, aCallback });
  1599             }
  1600             else
  1601             {
  1602                 //We are in the proper thread
  1603                 //Add this session to our collection of clients
  1604                 ClientData newClient = new ClientData(aSessionId, aCallback);
  1605                 Program.iMainForm.iClients.Add(aSessionId, newClient);
  1606                 //Add this session to our UI
  1607                 UpdateClientTreeViewNode(newClient);
  1608             }
  1609         }
  1610 
  1611 
  1612         /// <summary>
  1613         /// Find the client with the highest priority if any.
  1614         /// </summary>
  1615         /// <returns>Our highest priority client or null if not a single client is connected.</returns>
  1616         public ClientData FindHighestPriorityClient()
  1617         {
  1618             ClientData highestPriorityClient = null;
  1619             foreach (var client in iClients)
  1620             {
  1621                 if (highestPriorityClient == null || client.Value.Priority > highestPriorityClient.Priority)
  1622                 {
  1623                     highestPriorityClient = client.Value;
  1624                 }
  1625             }
  1626 
  1627             return highestPriorityClient;
  1628         }
  1629 
  1630         /// <summary>
  1631         ///
  1632         /// </summary>
  1633         /// <param name="aSessionId"></param>
  1634         public void RemoveClientThreadSafe(string aSessionId)
  1635         {
  1636             if (this.InvokeRequired)
  1637             {
  1638                 //Not in the proper thread, invoke ourselves
  1639                 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
  1640                 this.Invoke(d, new object[] { aSessionId });
  1641             }
  1642             else
  1643             {
  1644                 //We are in the proper thread
  1645                 //Remove this session from both client collection and UI tree view
  1646                 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
  1647                 {
  1648                     Program.iMainForm.iClients.Remove(aSessionId);
  1649                     Program.iMainForm.iTreeViewClients.Nodes.Remove(Program.iMainForm.iTreeViewClients.Nodes.Find(aSessionId, false)[0]);
  1650                     //Update recording status too whenever a client is removed
  1651                     UpdateRecordingNotification();
  1652                 }
  1653 
  1654                 if (iCurrentClientSessionId == aSessionId)
  1655                 {
  1656                     //The current client is closing
  1657                     iCurrentClientData = null;
  1658                     //Find the client with the highest priority and set it as current
  1659                     ClientData newCurrentClient = FindHighestPriorityClient();
  1660                     if (newCurrentClient!=null)
  1661                     {
  1662                         SetCurrentClient(newCurrentClient.SessionId, true);
  1663                     }                    
  1664                 }
  1665 
  1666                 if (iClients.Count == 0)
  1667 				{
  1668 					//Clear our screen when last client disconnects
  1669 					ClearLayout();
  1670 
  1671 					if (iClosing)
  1672 					{
  1673 						//We were closing our form
  1674 						//All clients are now closed
  1675 						//Just resume our close operation
  1676 						iClosing = false;
  1677 						Close();
  1678 					}
  1679 				}
  1680             }
  1681         }
  1682 
  1683         /// <summary>
  1684         ///
  1685         /// </summary>
  1686         /// <param name="aSessionId"></param>
  1687         /// <param name="aLayout"></param>
  1688         public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
  1689         {
  1690             if (this.InvokeRequired)
  1691             {
  1692                 //Not in the proper thread, invoke ourselves
  1693                 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
  1694                 this.Invoke(d, new object[] { aSessionId, aLayout });
  1695             }
  1696             else
  1697             {
  1698                 ClientData client = iClients[aSessionId];
  1699                 if (client != null)
  1700                 {
  1701                     //Don't change a thing if the layout is the same
  1702                     if (!client.Layout.IsSameAs(aLayout))
  1703                     {
  1704                         Debug.Print("SetClientLayoutThreadSafe: Layout updated.");
  1705                         //Set our client layout then
  1706                         client.Layout = aLayout;
  1707                         //So that next time we update all our fields at ones
  1708                         client.HasNewLayout = true;
  1709                         //Layout has changed clear our fields then
  1710                         client.Fields.Clear();
  1711                         //
  1712                         UpdateClientTreeViewNode(client);
  1713                     }
  1714                     else
  1715                     {
  1716                         Debug.Print("SetClientLayoutThreadSafe: Layout has not changed.");
  1717                     }
  1718                 }
  1719             }
  1720         }
  1721 
  1722         /// <summary>
  1723         ///
  1724         /// </summary>
  1725         /// <param name="aSessionId"></param>
  1726         /// <param name="aField"></param>
  1727         public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
  1728         {
  1729             if (this.InvokeRequired)
  1730             {
  1731                 //Not in the proper thread, invoke ourselves
  1732                 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
  1733                 this.Invoke(d, new object[] { aSessionId, aField });
  1734             }
  1735             else
  1736             {
  1737                 //We are in the proper thread
  1738                 //Call the non-thread-safe variant
  1739                 SetClientField(aSessionId, aField);
  1740             }
  1741         }
  1742 
  1743 
  1744 
  1745 
  1746         /// <summary>
  1747         /// Set a data field in the given client.
  1748         /// </summary>
  1749         /// <param name="aSessionId"></param>
  1750         /// <param name="aField"></param>
  1751         private void SetClientField(string aSessionId, DataField aField)
  1752         {   
  1753             //TODO: should check if the field actually changed?
  1754 
  1755             ClientData client = iClients[aSessionId];
  1756             bool layoutChanged = false;
  1757             bool contentChanged = true;
  1758 
  1759             //Fetch our field index
  1760             int fieldIndex = client.FindSameFieldIndex(aField);
  1761 
  1762             if (fieldIndex < 0)
  1763             {
  1764                 //No corresponding field, just bail out
  1765                 return;
  1766             }
  1767 
  1768             //Keep our previous field in there
  1769             DataField previousField = client.Fields[fieldIndex];
  1770             //Just update that field then 
  1771             client.Fields[fieldIndex] = aField;
  1772 
  1773             if (!aField.IsTableField)
  1774             {
  1775                 //We are done then if that field is not in our table layout
  1776                 return;
  1777             }
  1778 
  1779             TableField tableField = (TableField) aField;
  1780 
  1781             if (previousField.IsSameLayout(aField))
  1782             {
  1783                 //If we are updating a field in our current client we need to update it in our panel
  1784                 if (aSessionId == iCurrentClientSessionId)
  1785                 {
  1786                     Control ctrl=iTableLayoutPanel.GetControlFromPosition(tableField.Column, tableField.Row);
  1787                     if (aField.IsTextField && ctrl is MarqueeLabel)
  1788                     {
  1789                         TextField textField=(TextField)aField;
  1790                         //Text field control already in place, just change the text
  1791                         MarqueeLabel label = (MarqueeLabel)ctrl;
  1792                         contentChanged = (label.Text != textField.Text || label.TextAlign != textField.Alignment);
  1793                         label.Text = textField.Text;
  1794                         label.TextAlign = textField.Alignment;
  1795                     }
  1796                     else if (aField.IsBitmapField && ctrl is PictureBox)
  1797                     {
  1798                         BitmapField bitmapField = (BitmapField)aField;
  1799                         contentChanged = true; //TODO: Bitmap comp or should we leave that to clients?
  1800                         //Bitmap field control already in place just change the bitmap
  1801                         PictureBox pictureBox = (PictureBox)ctrl;
  1802                         pictureBox.Image = bitmapField.Bitmap;
  1803                     }
  1804                     else
  1805                     {
  1806                         layoutChanged = true;
  1807                     }
  1808                 }
  1809             }
  1810             else
  1811             {                
  1812                 layoutChanged = true;
  1813             }
  1814 
  1815             //If either content or layout changed we need to update our tree view to reflect the changes
  1816             if (contentChanged || layoutChanged)
  1817             {
  1818                 UpdateClientTreeViewNode(client);
  1819                 //
  1820                 if (layoutChanged)
  1821                 {
  1822                     Debug.Print("Layout changed");
  1823                     //Our layout has changed, if we are already the current client we need to update our panel
  1824                     if (aSessionId == iCurrentClientSessionId)
  1825                     {
  1826                         //Apply layout and set data fields.
  1827                         UpdateTableLayoutPanel(iCurrentClientData);
  1828                     }
  1829                 }
  1830                 else
  1831                 {
  1832                     Debug.Print("Layout has not changed.");
  1833                 }
  1834             }
  1835             else
  1836             {
  1837                 Debug.Print("WARNING: content and layout have not changed!");
  1838             }
  1839 
  1840             //When a client field is set we try switching to this client to present the new information to our user
  1841             SetCurrentClient(aSessionId);
  1842         }
  1843 
  1844         /// <summary>
  1845         ///
  1846         /// </summary>
  1847         /// <param name="aTexts"></param>
  1848         public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
  1849         {
  1850             if (this.InvokeRequired)
  1851             {
  1852                 //Not in the proper thread, invoke ourselves
  1853                 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
  1854                 this.Invoke(d, new object[] { aSessionId, aFields });
  1855             }
  1856             else
  1857             {
  1858                 ClientData client = iClients[aSessionId];
  1859 
  1860                 if (client.HasNewLayout)
  1861                 {
  1862                     //TODO: Assert client.Count == 0
  1863                     //Our layout was just changed
  1864                     //Do some special handling to avoid re-creating our panel N times, once for each fields
  1865                     client.HasNewLayout = false;
  1866                     //Just set all our fields then
  1867                     client.Fields.AddRange(aFields);
  1868                     //Try switch to that client
  1869                     SetCurrentClient(aSessionId);
  1870 
  1871                     //If we are updating the current client update our panel
  1872                     if (aSessionId == iCurrentClientSessionId)
  1873                     {
  1874                         //Apply layout and set data fields.
  1875                         UpdateTableLayoutPanel(iCurrentClientData);
  1876                     }
  1877 
  1878                     UpdateClientTreeViewNode(client);
  1879                 }
  1880                 else
  1881                 {
  1882                     //Put each our text fields in a label control
  1883                     foreach (DataField field in aFields)
  1884                     {
  1885                         SetClientField(aSessionId, field);
  1886                     }
  1887                 }
  1888             }
  1889         }
  1890 
  1891         /// <summary>
  1892         ///
  1893         /// </summary>
  1894         /// <param name="aSessionId"></param>
  1895         /// <param name="aName"></param>
  1896         public void SetClientNameThreadSafe(string aSessionId, string aName)
  1897         {
  1898             if (this.InvokeRequired)
  1899             {
  1900                 //Not in the proper thread, invoke ourselves
  1901                 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
  1902                 this.Invoke(d, new object[] { aSessionId, aName });
  1903             }
  1904             else
  1905             {
  1906                 //We are in the proper thread
  1907                 //Get our client
  1908                 ClientData client = iClients[aSessionId];
  1909                 if (client != null)
  1910                 {
  1911                     //Set its name
  1912                     client.Name = aName;
  1913                     //Update our tree-view
  1914                     UpdateClientTreeViewNode(client);
  1915                 }
  1916             }
  1917         }
  1918 
  1919         ///
  1920         public void SetClientPriorityThreadSafe(string aSessionId, uint aPriority)
  1921         {
  1922             if (this.InvokeRequired)
  1923             {
  1924                 //Not in the proper thread, invoke ourselves
  1925                 SetClientPriorityDelegate d = new SetClientPriorityDelegate(SetClientPriorityThreadSafe);
  1926                 this.Invoke(d, new object[] { aSessionId, aPriority });
  1927             }
  1928             else
  1929             {
  1930                 //We are in the proper thread
  1931                 //Get our client
  1932                 ClientData client = iClients[aSessionId];
  1933                 if (client != null)
  1934                 {
  1935                     //Set its name
  1936                     client.Priority = aPriority;
  1937                     //Update our tree-view
  1938                     UpdateClientTreeViewNode(client);
  1939                     //Change our current client as per new priority
  1940                     ClientData newCurrentClient = FindHighestPriorityClient();
  1941                     if (newCurrentClient!=null)
  1942                     {
  1943                         SetCurrentClient(newCurrentClient.SessionId);
  1944                     }
  1945                 }
  1946             }
  1947         }
  1948 
  1949         /// <summary>
  1950         /// 
  1951         /// </summary>
  1952         /// <param name="value"></param>
  1953         /// <param name="maxChars"></param>
  1954         /// <returns></returns>
  1955         public static string Truncate(string value, int maxChars)
  1956         {
  1957             return value.Length <= maxChars ? value : value.Substring(0, maxChars-3) + "...";
  1958         }
  1959 
  1960         /// <summary>
  1961         /// Update our recording notification.
  1962         /// </summary>
  1963         private void UpdateRecordingNotification()
  1964         {
  1965             //Go through each 
  1966             bool activeRecording = false;
  1967             string text="";
  1968             RecordingField recField=new RecordingField();
  1969             foreach (var client in iClients)
  1970             {
  1971                 RecordingField rec=(RecordingField)client.Value.FindSameFieldAs(recField);
  1972                 if (rec!=null && rec.IsActive)
  1973                 {
  1974                     activeRecording = true;
  1975                     //Don't break cause we are collecting the names/texts.
  1976                     if (!String.IsNullOrEmpty(rec.Text))
  1977                     {
  1978                         text += (rec.Text + "\n");
  1979                     }
  1980                     else
  1981                     {
  1982                         //Not text for that recording, use client name instead
  1983                         text += client.Value.Name + " recording\n";
  1984                     }
  1985                     
  1986                 }
  1987             }
  1988 
  1989             //Update our text no matter what, can't have more than 63 characters otherwise it throws an exception.
  1990             iRecordingNotification.Text = Truncate(text,63);
  1991 
  1992             //Change visibility of notification if needed
  1993             if (iRecordingNotification.Visible != activeRecording)
  1994             {                
  1995                 iRecordingNotification.Visible = activeRecording;
  1996                 //Assuming the notification icon is in sync with our display icon
  1997                 //Take care of our REC icon
  1998                 if (iDisplay.IsOpen())
  1999                 {
  2000                     iDisplay.SetIconOnOff(MiniDisplay.IconType.Recording, activeRecording);
  2001                 }                
  2002             }
  2003         }
  2004 
  2005         /// <summary>
  2006         ///
  2007         /// </summary>
  2008         /// <param name="aClient"></param>
  2009         private void UpdateClientTreeViewNode(ClientData aClient)
  2010         {
  2011             Debug.Print("UpdateClientTreeViewNode");
  2012 
  2013             if (aClient == null)
  2014             {
  2015                 return;
  2016             }
  2017 
  2018             //Hook in record icon update too
  2019             UpdateRecordingNotification();
  2020 
  2021             TreeNode node = null;
  2022             //Check that our client node already exists
  2023             //Get our client root node using its key which is our session ID
  2024             TreeNode[] nodes = iTreeViewClients.Nodes.Find(aClient.SessionId, false);
  2025             if (nodes.Count()>0)
  2026             {
  2027                 //We already have a node for that client
  2028                 node = nodes[0];
  2029                 //Clear children as we are going to recreate them below
  2030                 node.Nodes.Clear();
  2031             }
  2032             else
  2033             {
  2034                 //Client node does not exists create a new one
  2035                 iTreeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
  2036                 node = iTreeViewClients.Nodes.Find(aClient.SessionId, false)[0];
  2037             }
  2038 
  2039             if (node != null)
  2040             {
  2041                 //Change its name
  2042                 if (!String.IsNullOrEmpty(aClient.Name))
  2043                 {
  2044                     //We have a name, use it as text for our root node
  2045                     node.Text = aClient.Name;
  2046                     //Add a child with SessionId
  2047                     node.Nodes.Add(new TreeNode(aClient.SessionId));
  2048                 }
  2049                 else
  2050                 {
  2051                     //No name, use session ID instead
  2052                     node.Text = aClient.SessionId;
  2053                 }
  2054 
  2055                 //Display client priority
  2056                 node.Nodes.Add(new TreeNode("Priority: " + aClient.Priority));
  2057 
  2058                 if (aClient.Fields.Count > 0)
  2059                 {
  2060                     //Create root node for our texts
  2061                     TreeNode textsRoot = new TreeNode("Fields");
  2062                     node.Nodes.Add(textsRoot);
  2063                     //For each text add a new entry
  2064                     foreach (DataField field in aClient.Fields)
  2065                     {
  2066                         if (field.IsTextField)
  2067                         {
  2068                             TextField textField = (TextField)field;
  2069                             textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
  2070                         }
  2071                         else if (field.IsBitmapField)
  2072                         {
  2073                             textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
  2074                         }
  2075                         else if (field.IsRecordingField)
  2076                         {
  2077                             RecordingField recordingField = (RecordingField)field;
  2078                             textsRoot.Nodes.Add(new TreeNode("[Recording]" + recordingField.IsActive));
  2079                         }
  2080                     }
  2081                 }
  2082 
  2083                 node.ExpandAll();
  2084             }
  2085         }
  2086 
  2087         /// <summary>
  2088         /// Update our table layout row styles to make sure each rows have similar height
  2089         /// </summary>
  2090         private void UpdateTableLayoutRowStyles()
  2091         {
  2092             foreach (RowStyle rowStyle in iTableLayoutPanel.RowStyles)
  2093             {
  2094                 rowStyle.SizeType = SizeType.Percent;
  2095                 rowStyle.Height = 100 / iTableLayoutPanel.RowCount;
  2096             }
  2097         }
  2098 
  2099         /// <summary>
  2100         /// Update our display table layout.
  2101         /// Will instanciated every field control as defined by our client.
  2102         /// Fields must be specified by rows from the left.
  2103         /// </summary>
  2104         /// <param name="aLayout"></param>
  2105         private void UpdateTableLayoutPanel(ClientData aClient)
  2106         {
  2107             Debug.Print("UpdateTableLayoutPanel");
  2108 
  2109 			if (aClient == null)
  2110 			{
  2111 				//Just drop it
  2112 				return;
  2113 			}
  2114 
  2115 
  2116             TableLayout layout = aClient.Layout;
  2117 
  2118             //First clean our current panel
  2119             iTableLayoutPanel.Controls.Clear();
  2120             iTableLayoutPanel.RowStyles.Clear();
  2121             iTableLayoutPanel.ColumnStyles.Clear();
  2122             iTableLayoutPanel.RowCount = 0;
  2123             iTableLayoutPanel.ColumnCount = 0;
  2124 
  2125             //Then recreate our rows...
  2126             while (iTableLayoutPanel.RowCount < layout.Rows.Count)
  2127             {
  2128                 iTableLayoutPanel.RowCount++;
  2129             }
  2130 
  2131             // ...and columns 
  2132             while (iTableLayoutPanel.ColumnCount < layout.Columns.Count)
  2133             {
  2134                 iTableLayoutPanel.ColumnCount++;
  2135             }
  2136 
  2137             //For each column
  2138             for (int i = 0; i < iTableLayoutPanel.ColumnCount; i++)
  2139             {
  2140                 //Create our column styles
  2141                 this.iTableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
  2142 
  2143                 //For each rows
  2144                 for (int j = 0; j < iTableLayoutPanel.RowCount; j++)
  2145                 {
  2146                     if (i == 0)
  2147                     {
  2148                         //Create our row styles
  2149                         this.iTableLayoutPanel.RowStyles.Add(layout.Rows[j]);
  2150                     }
  2151                     else
  2152                     {
  2153                         continue;
  2154                     }
  2155                 }
  2156             }
  2157 
  2158             //For each field
  2159             foreach (DataField field in aClient.Fields)
  2160             {
  2161                 if (!field.IsTableField)
  2162                 {
  2163                     //That field is not taking part in our table layout skip it
  2164                     continue;
  2165                 }
  2166 
  2167                 TableField tableField = (TableField)field;
  2168 
  2169                 //Create a control corresponding to the field specified for that cell
  2170                 Control control = CreateControlForDataField(tableField);
  2171 
  2172                 //Add newly created control to our table layout at the specified row and column
  2173                 iTableLayoutPanel.Controls.Add(control, tableField.Column, tableField.Row);
  2174                 //Make sure we specify column and row span for that new control
  2175                 iTableLayoutPanel.SetColumnSpan(control, tableField.ColumnSpan);
  2176                 iTableLayoutPanel.SetRowSpan(control, tableField.RowSpan);
  2177             }
  2178 
  2179 
  2180             CheckFontHeight();
  2181         }
  2182 
  2183         /// <summary>
  2184         /// Check our type of data field and create corresponding control
  2185         /// </summary>
  2186         /// <param name="aField"></param>
  2187         private Control CreateControlForDataField(DataField aField)
  2188         {
  2189             Control control=null;
  2190             if (aField.IsTextField)
  2191             {
  2192                 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
  2193                 label.AutoEllipsis = true;
  2194                 label.AutoSize = true;
  2195                 label.BackColor = System.Drawing.Color.Transparent;
  2196                 label.Dock = System.Windows.Forms.DockStyle.Fill;
  2197                 label.Location = new System.Drawing.Point(1, 1);
  2198                 label.Margin = new System.Windows.Forms.Padding(0);
  2199                 label.Name = "marqueeLabel" + aField;
  2200                 label.OwnTimer = false;
  2201                 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
  2202                 label.Separator = cds.Separator;
  2203                 label.MinFontSize = cds.MinFontSize;
  2204                 label.ScaleToFit = cds.ScaleToFit;
  2205                 //control.Size = new System.Drawing.Size(254, 30);
  2206                 //control.TabIndex = 2;
  2207                 label.Font = cds.Font;
  2208 
  2209                 TextField field = (TextField)aField;
  2210                 label.TextAlign = field.Alignment;
  2211                 label.UseCompatibleTextRendering = true;
  2212                 label.Text = field.Text;
  2213                 //
  2214                 control = label;
  2215             }
  2216             else if (aField.IsBitmapField)
  2217             {
  2218                 //Create picture box
  2219                 PictureBox picture = new PictureBox();
  2220                 picture.AutoSize = true;
  2221                 picture.BackColor = System.Drawing.Color.Transparent;
  2222                 picture.Dock = System.Windows.Forms.DockStyle.Fill;
  2223                 picture.Location = new System.Drawing.Point(1, 1);
  2224                 picture.Margin = new System.Windows.Forms.Padding(0);
  2225                 picture.Name = "pictureBox" + aField;
  2226                 //Set our image
  2227                 BitmapField field = (BitmapField)aField;
  2228                 picture.Image = field.Bitmap;
  2229                 //
  2230                 control = picture;
  2231             }
  2232             //TODO: Handle recording field?
  2233 
  2234             return control;
  2235         }
  2236 
  2237 		/// <summary>
  2238 		/// Called when the user selected a new display type.
  2239 		/// </summary>
  2240 		/// <param name="sender"></param>
  2241 		/// <param name="e"></param>
  2242         private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
  2243         {
  2244 			//Store the selected display type in our settings
  2245             Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
  2246             cds.DisplayType = comboBoxDisplayType.SelectedIndex;
  2247             Properties.Settings.Default.Save();
  2248 
  2249 			//Try re-opening the display connection if we were already connected.
  2250 			//Otherwise just update our status to reflect display type change.
  2251             if (iDisplay.IsOpen())
  2252             {
  2253                 OpenDisplayConnection();
  2254             }
  2255             else
  2256             {
  2257                 UpdateStatus();
  2258             }
  2259         }
  2260 
  2261         private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
  2262         {
  2263             if (maskedTextBoxTimerInterval.Text != "")
  2264             {
  2265                 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
  2266 
  2267                 if (interval > 0)
  2268                 {
  2269                     timer.Interval = interval;
  2270                     cds.TimerInterval = timer.Interval;
  2271                     Properties.Settings.Default.Save();
  2272                 }
  2273             }
  2274         }
  2275 
  2276         private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
  2277         {
  2278             if (maskedTextBoxMinFontSize.Text != "")
  2279             {
  2280                 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
  2281 
  2282                 if (minFontSize > 0)
  2283                 {
  2284                     cds.MinFontSize = minFontSize;
  2285                     Properties.Settings.Default.Save();
  2286 					//We need to recreate our layout for that change to take effect
  2287 					UpdateTableLayoutPanel(iCurrentClientData);
  2288                 }
  2289             }
  2290         }
  2291 
  2292 
  2293 		private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
  2294 		{
  2295 			if (maskedTextBoxScrollingSpeed.Text != "")
  2296 			{
  2297 				int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
  2298 
  2299 				if (scrollingSpeed > 0)
  2300 				{
  2301 					cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
  2302 					Properties.Settings.Default.Save();
  2303 					//We need to recreate our layout for that change to take effect
  2304 					UpdateTableLayoutPanel(iCurrentClientData);
  2305 				}
  2306 			}
  2307 		}
  2308 
  2309         private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
  2310         {
  2311             cds.Separator = textBoxScrollLoopSeparator.Text;
  2312             Properties.Settings.Default.Save();
  2313 
  2314 			//Update our text fields
  2315 			foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls)
  2316 			{
  2317 				ctrl.Separator = cds.Separator;
  2318 			}
  2319 
  2320         }
  2321 
  2322         private void buttonPowerOn_Click(object sender, EventArgs e)
  2323         {
  2324             iDisplay.PowerOn();
  2325         }
  2326 
  2327         private void buttonPowerOff_Click(object sender, EventArgs e)
  2328         {
  2329             iDisplay.PowerOff();
  2330         }
  2331 
  2332         private void buttonShowClock_Click(object sender, EventArgs e)
  2333         {
  2334 			ShowClock();
  2335         }
  2336 
  2337         private void buttonHideClock_Click(object sender, EventArgs e)
  2338         {
  2339 			HideClock();
  2340         }
  2341 
  2342         private void buttonUpdate_Click(object sender, EventArgs e)
  2343         {
  2344             InstallUpdateSyncWithInfo();
  2345         }
  2346 
  2347 		/// <summary>
  2348 		/// 
  2349 		/// </summary>
  2350 		void ShowClock()
  2351 		{
  2352 			if (!iDisplay.IsOpen())
  2353 			{
  2354 				return;
  2355 			}
  2356 
  2357 			//Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
  2358 			iSkipFrameRendering = true;
  2359 			//Clear our screen 
  2360 			iDisplay.Clear();
  2361 			iDisplay.SwapBuffers();
  2362 			//Then show our clock
  2363 			iDisplay.ShowClock();
  2364 		}
  2365 
  2366 		/// <summary>
  2367 		/// 
  2368 		/// </summary>
  2369 		void HideClock()
  2370 		{
  2371 			if (!iDisplay.IsOpen())
  2372 			{
  2373 				return;
  2374 			}
  2375 
  2376 			//Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
  2377 			iSkipFrameRendering = false;
  2378 			iDisplay.HideClock();
  2379 		}
  2380 
  2381         private void InstallUpdateSyncWithInfo()
  2382         {
  2383             UpdateCheckInfo info = null;
  2384 
  2385             if (ApplicationDeployment.IsNetworkDeployed)
  2386             {
  2387                 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
  2388 
  2389                 try
  2390                 {
  2391                     info = ad.CheckForDetailedUpdate();
  2392 
  2393                 }
  2394                 catch (DeploymentDownloadException dde)
  2395                 {
  2396                     MessageBox.Show("The new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " + dde.Message);
  2397                     return;
  2398                 }
  2399                 catch (InvalidDeploymentException ide)
  2400                 {
  2401                     MessageBox.Show("Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " + ide.Message);
  2402                     return;
  2403                 }
  2404                 catch (InvalidOperationException ioe)
  2405                 {
  2406                     MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
  2407                     return;
  2408                 }
  2409 
  2410 				if (info.UpdateAvailable)
  2411 				{
  2412 					Boolean doUpdate = true;
  2413 
  2414 					if (!info.IsUpdateRequired)
  2415 					{
  2416 						DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
  2417 						if (!(DialogResult.OK == dr))
  2418 						{
  2419 							doUpdate = false;
  2420 						}
  2421 					}
  2422 					else
  2423 					{
  2424 						// Display a message that the application MUST reboot. Display the minimum required version.
  2425 						MessageBox.Show("This application has detected a mandatory update from your current " +
  2426 							"version to version " + info.MinimumRequiredVersion.ToString() +
  2427 							". The application will now install the update and restart.",
  2428 							"Update Available", MessageBoxButtons.OK,
  2429 							MessageBoxIcon.Information);
  2430 					}
  2431 
  2432 					if (doUpdate)
  2433 					{
  2434 						try
  2435 						{
  2436 							ad.Update();
  2437 							MessageBox.Show("The application has been upgraded, and will now restart.");
  2438 							Application.Restart();
  2439 						}
  2440 						catch (DeploymentDownloadException dde)
  2441 						{
  2442 							MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
  2443 							return;
  2444 						}
  2445 					}
  2446 				}
  2447 				else
  2448 				{
  2449 					MessageBox.Show("You are already running the latest version.", "Application up-to-date");
  2450 				}
  2451             }
  2452         }
  2453 
  2454 
  2455 		/// <summary>
  2456 		/// Used to
  2457 		/// </summary>
  2458 		private void SysTrayHideShow()
  2459 		{
  2460 			Visible = !Visible;
  2461 			if (Visible)
  2462 			{
  2463 				Activate();
  2464 				WindowState = FormWindowState.Normal;
  2465 			}
  2466 		}
  2467 
  2468 		/// <summary>
  2469 		/// Use to handle minimize events.
  2470 		/// </summary>
  2471 		/// <param name="sender"></param>
  2472 		/// <param name="e"></param>
  2473 		private void MainForm_SizeChanged(object sender, EventArgs e)
  2474 		{
  2475 			if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
  2476 			{
  2477 				if (Visible)
  2478 				{
  2479 					SysTrayHideShow();
  2480 				}
  2481 			}
  2482 		}
  2483 
  2484 		/// <summary>
  2485 		/// 
  2486 		/// </summary>
  2487 		/// <param name="sender"></param>
  2488 		/// <param name="e"></param>
  2489 		private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
  2490 		{
  2491 			//Our table layout size has changed which means our display size has changed.
  2492 			//We need to re-create our bitmap.
  2493 			iCreateBitmap = true;
  2494 		}
  2495 
  2496 		/// <summary>
  2497 		/// 
  2498 		/// </summary>
  2499 		/// <param name="sender"></param>
  2500 		/// <param name="e"></param>
  2501 		private void buttonSelectFile_Click(object sender, EventArgs e)
  2502 		{
  2503 			//openFileDialog1.InitialDirectory = "c:\\";
  2504 			//openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
  2505 			//openFileDialog.FilterIndex = 1;
  2506 			openFileDialog.RestoreDirectory = true;
  2507 
  2508 			if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
  2509 			{
  2510 				labelStartFileName.Text = openFileDialog.FileName;
  2511 				Properties.Settings.Default.StartFileName = openFileDialog.FileName;
  2512 				Properties.Settings.Default.Save();
  2513 			}
  2514 		}
  2515 
  2516         /// <summary>
  2517         /// 
  2518         /// </summary>
  2519         /// <param name="sender"></param>
  2520         /// <param name="e"></param>
  2521         private void comboBoxOpticalDrives_SelectedIndexChanged(object sender, EventArgs e)
  2522         {
  2523             //Save the optical drive the user selected for ejection
  2524             Properties.Settings.Default.OpticalDriveToEject = comboBoxOpticalDrives.SelectedItem.ToString();
  2525             Properties.Settings.Default.Save();
  2526         }
  2527 
  2528 
  2529         /// <summary>
  2530         /// 
  2531         /// </summary>
  2532         private void LogsUpdate()
  2533         {
  2534             if (iWriter != null)
  2535             {
  2536                 iWriter.Flush();
  2537             }
  2538 
  2539         }
  2540 
  2541         /// <summary>
  2542         /// Broadcast messages to subscribers.
  2543         /// </summary>
  2544         /// <param name="message"></param>
  2545         protected override void WndProc(ref Message aMessage)
  2546         {
  2547             LogsUpdate();
  2548 
  2549             if (OnWndProc!=null)
  2550             {
  2551                 OnWndProc(ref aMessage);
  2552             }
  2553             
  2554             base.WndProc(ref aMessage);
  2555         }
  2556 
  2557         private void checkBoxCecEnabled_CheckedChanged(object sender, EventArgs e)
  2558         {
  2559             //Save CEC enabled status
  2560             Properties.Settings.Default.CecEnabled = checkBoxCecEnabled.Checked;
  2561             Properties.Settings.Default.Save();
  2562             //
  2563             ResetCec();
  2564         }
  2565 
  2566         private void comboBoxHdmiPort_SelectedIndexChanged(object sender, EventArgs e)
  2567         {
  2568             //Save CEC HDMI port
  2569             Properties.Settings.Default.CecHdmiPort = Convert.ToByte(comboBoxHdmiPort.SelectedIndex);
  2570             Properties.Settings.Default.CecHdmiPort++;
  2571             Properties.Settings.Default.Save();
  2572             //
  2573             ResetCec();
  2574         }
  2575 
  2576         private void checkBoxCecMonitorOff_CheckedChanged(object sender, EventArgs e)
  2577         {
  2578             Properties.Settings.Default.CecMonitorOff = checkBoxCecMonitorOff.Checked;
  2579             Properties.Settings.Default.Save();
  2580             //
  2581             ResetCec();
  2582         }
  2583 
  2584         private void checkBoxCecMonitorOn_CheckedChanged(object sender, EventArgs e)
  2585         {
  2586             Properties.Settings.Default.CecMonitorOn = checkBoxCecMonitorOn.Checked;
  2587             Properties.Settings.Default.Save();
  2588             //
  2589             ResetCec();
  2590         }
  2591 
  2592         private void checkBoxCecReconnectToPowerTv_CheckedChanged(object sender, EventArgs e)
  2593         {
  2594             Properties.Settings.Default.CecReconnectToPowerTv = checkBoxCecReconnectToPowerTv.Checked;
  2595             Properties.Settings.Default.Save();
  2596             //
  2597             ResetCec();
  2598         }
  2599 
  2600         /// <summary>
  2601         /// 
  2602         /// </summary>
  2603         private void ResetCec()
  2604         {
  2605             if (iCecManager==null)
  2606             {
  2607                 //Thus skipping initial UI setup
  2608                 return;
  2609             }
  2610 
  2611             iCecManager.Stop();
  2612             //
  2613             if (Properties.Settings.Default.CecEnabled)
  2614             {
  2615                 iCecManager.Start(Handle, "CEC",
  2616                 Properties.Settings.Default.CecHdmiPort,
  2617                 Properties.Settings.Default.CecMonitorOn,
  2618                 Properties.Settings.Default.CecMonitorOff,
  2619                 Properties.Settings.Default.CecReconnectToPowerTv);
  2620 
  2621                 SetupCecLogLevel();
  2622             }
  2623         }
  2624 
  2625         /// <summary>
  2626         /// 
  2627         /// </summary>
  2628         private void SetupCecLogLevel()
  2629         {
  2630             //Setup log level
  2631             iCecManager.Client.LogLevel = 0;
  2632 
  2633             if (checkBoxCecLogError.Checked)
  2634                 iCecManager.Client.LogLevel |= (int)CecLogLevel.Error;
  2635 
  2636             if (checkBoxCecLogWarning.Checked)
  2637                 iCecManager.Client.LogLevel |= (int)CecLogLevel.Warning;
  2638 
  2639             if (checkBoxCecLogNotice.Checked)
  2640                 iCecManager.Client.LogLevel |= (int)CecLogLevel.Notice;
  2641 
  2642             if (checkBoxCecLogTraffic.Checked)
  2643                 iCecManager.Client.LogLevel |= (int)CecLogLevel.Traffic;
  2644 
  2645             if (checkBoxCecLogDebug.Checked)
  2646                 iCecManager.Client.LogLevel |= (int)CecLogLevel.Debug;
  2647 
  2648             iCecManager.Client.FilterOutPollLogs = checkBoxCecLogNoPoll.Checked;
  2649 
  2650         }
  2651 
  2652         private void ButtonStartIdleClient_Click(object sender, EventArgs e)
  2653         {
  2654             StartIdleClient();
  2655         }
  2656 
  2657         private void buttonClearLogs_Click(object sender, EventArgs e)
  2658         {
  2659             richTextBoxLogs.Clear();
  2660         }
  2661 
  2662         private void checkBoxCecLogs_CheckedChanged(object sender, EventArgs e)
  2663         {
  2664             SetupCecLogLevel();
  2665         }
  2666 
  2667         private void buttonAddAction_Click(object sender, EventArgs e)
  2668         {
  2669             if (iTreeViewEvents.SelectedNode==null)
  2670             {
  2671                 return;
  2672             }
  2673 
  2674             Event earEvent = (Event)iTreeViewEvents.SelectedNode.Tag;
  2675             if (earEvent == null)
  2676             {
  2677                 //Must select event node
  2678                 return;
  2679             }
  2680 
  2681             FormEditAction ea = new FormEditAction();
  2682             DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea);
  2683             if (res == DialogResult.OK)
  2684             {
  2685                 earEvent.Actions.Add(ea.Action);                
  2686                 Properties.Settings.Default.Actions = ManagerEventAction.Current;
  2687                 Properties.Settings.Default.Save();
  2688                 SetupEvents();
  2689             }
  2690         }
  2691     }
  2692 }