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