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