Server/MainForm.cs
author StephaneLenclud
Sun, 08 Feb 2015 18:34:00 +0100
changeset 113 0846e5112dd7
parent 112 57b1c6507bd7
child 114 debe115b3b22
permissions -rw-r--r--
Audio: adding support for mute status.
     1 using System;
     2 using System.Collections.Generic;
     3 using System.ComponentModel;
     4 using System.Data;
     5 using System.Drawing;
     6 using System.Linq;
     7 using System.Text;
     8 using System.Threading.Tasks;
     9 using System.Windows.Forms;
    10 using System.IO;
    11 using CodeProject.Dialog;
    12 using System.Drawing.Imaging;
    13 using System.ServiceModel;
    14 using System.Threading;
    15 using System.Diagnostics;
    16 using System.Deployment.Application;
    17 using System.Reflection;
    18 //NAudio
    19 using NAudio.CoreAudioApi;
    20 using NAudio.CoreAudioApi.Interfaces;
    21 using System.Runtime.InteropServices;
    22 //
    23 using SharpDisplayClient;
    24 using SharpDisplay;
    25 
    26 namespace SharpDisplayManager
    27 {
    28     //Types declarations
    29     public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
    30     public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
    31     //Delegates are used for our thread safe method
    32     public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
    33     public delegate void RemoveClientDelegate(string aSessionId);
    34     public delegate void SetFieldDelegate(string SessionId, DataField aField);
    35     public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
    36     public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
    37     public delegate void SetClientNameDelegate(string aSessionId, string aName);
    38 	public delegate void PlainUpdateDelegate();
    39 
    40 
    41     /// <summary>
    42     /// Our Display manager main form
    43     /// </summary>
    44 	public partial class MainForm : Form, IMMNotificationClient
    45     {
    46         DateTime LastTickTime;
    47         Display iDisplay;
    48         System.Drawing.Bitmap iBmp;
    49         bool iCreateBitmap; //Workaround render to bitmap issues when minimized
    50         ServiceHost iServiceHost;
    51         // Our collection of clients sorted by session id.
    52         public Dictionary<string, ClientData> iClients;
    53         // The name of the client which informations are currently displayed.
    54         public string iCurrentClientSessionId;
    55         ClientData iCurrentClientData;
    56         //
    57         public bool iClosing;
    58         //Function pointer for pixel color filtering
    59         ColorProcessingDelegate iColorFx;
    60         //Function pointer for pixel X coordinate intercept
    61         CoordinateTranslationDelegate iScreenX;
    62         //Function pointer for pixel Y coordinate intercept
    63         CoordinateTranslationDelegate iScreenY;
    64 		//NAudio
    65 		private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
    66 		private MMDevice iMultiMediaDevice;
    67 		
    68 
    69 		/// <summary>
    70 		/// Manage run when Windows startup option
    71 		/// </summary>
    72 		private StartupManager iStartupManager;
    73 
    74 		/// <summary>
    75 		/// System tray icon.
    76 		/// </summary>
    77 		private NotifyIconAdv iNotifyIcon;
    78 
    79         public MainForm()
    80         {
    81             iCurrentClientSessionId = "";
    82             iCurrentClientData = null;
    83             LastTickTime = DateTime.Now;
    84 			//Instantiate our display and register for events notifications
    85             iDisplay = new Display();
    86 			iDisplay.OnOpened += OnDisplayOpened;
    87 			iDisplay.OnClosed += OnDisplayClosed;
    88 			//
    89 			iClients = new Dictionary<string, ClientData>();
    90 			iStartupManager = new StartupManager();
    91 			iNotifyIcon = new NotifyIconAdv();
    92 
    93 			//Have our designer initialize its controls
    94             InitializeComponent();
    95 
    96 			//Populate device types
    97 			PopulateDeviceTypes();
    98 
    99 			//Initial status update 
   100             UpdateStatus();
   101 
   102             //We have a bug when drawing minimized and reusing our bitmap
   103             iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   104             iCreateBitmap = false;
   105 
   106 			//Minimize our window if desired
   107 			if (Properties.Settings.Default.StartMinimized)
   108 			{
   109 				WindowState = FormWindowState.Minimized;
   110 			}
   111 
   112         }
   113 
   114 		/// <summary>
   115 		///
   116 		/// </summary>
   117 		/// <param name="sender"></param>
   118 		/// <param name="e"></param>
   119         private void MainForm_Load(object sender, EventArgs e)
   120         {
   121 			//Check if we are running a Click Once deployed application
   122 			if (ApplicationDeployment.IsNetworkDeployed)
   123 			{
   124 				//This is a proper Click Once installation, fetch and show our version number
   125 				this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
   126 			}
   127 			else
   128 			{
   129 				//Not a proper Click Once installation, assuming development build then
   130 				this.Text += " - development";
   131 			}
   132 
   133 			//NAudio
   134 			iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
   135 			iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
   136 			
   137 			UpdateAudioDeviceAndMasterVolumeThreadSafe();
   138 
   139 			//Setup notification icon
   140 			SetupTrayIcon();
   141 
   142 			// To make sure start up with minimize to tray works
   143 			if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
   144 			{
   145 				Visible = false;
   146 			}
   147 
   148 #if !DEBUG
   149 			//When not debugging we want the screen to be empty until a client takes over
   150 			ClearLayout();
   151 #else
   152 			//When developing we want at least one client for testing
   153 			StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
   154 #endif
   155 
   156 			//Open display connection on start-up if needed
   157 			if (Properties.Settings.Default.DisplayConnectOnStartup)
   158 			{
   159 				OpenDisplayConnection();
   160 			}
   161 
   162 			//Start our server so that we can get client requests
   163 			StartServer();
   164         }
   165 
   166 		/// <summary>
   167 		/// Called when our display is opened.
   168 		/// </summary>
   169 		/// <param name="aDisplay"></param>
   170 		private void OnDisplayOpened(Display aDisplay)
   171 		{
   172 			//Set our screen size now that our display is connected
   173 			//Our panelDisplay is the container of our tableLayoutPanel
   174 			//tableLayoutPanel will resize itself to fit the client size of our panelDisplay
   175 			//panelDisplay needs an extra 2 pixels for borders on each sides
   176 			//tableLayoutPanel will eventually be the exact size of our display
   177 			Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
   178 			panelDisplay.Size = size;
   179 
   180 			//Our display was just opened, update our UI
   181 			UpdateStatus();
   182 			//Initiate asynchronous request
   183 			iDisplay.RequestFirmwareRevision();
   184 
   185 			//
   186 			UpdateMasterVolumeThreadSafe();
   187 
   188 #if DEBUG
   189 			//Testing icon in debug, no arm done if icon not supported
   190 			//iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
   191 			//iDisplay.SetAllIconsStatus(2);
   192 #endif
   193 
   194 		}
   195 
   196 		/// <summary>
   197 		/// Called when our display is closed.
   198 		/// </summary>
   199 		/// <param name="aDisplay"></param>
   200 		private void OnDisplayClosed(Display aDisplay)
   201 		{
   202 			//Our display was just closed, update our UI consequently
   203 			UpdateStatus();
   204 		}
   205 		
   206         /// <summary>
   207         /// Receive volume change notification and reflect changes on our slider.
   208         /// </summary>
   209         /// <param name="data"></param>
   210         public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
   211         {
   212 			UpdateMasterVolumeThreadSafe();
   213         }
   214 
   215         /// <summary>
   216         /// Update master volume when user moves our slider.
   217         /// </summary>
   218         /// <param name="sender"></param>
   219         /// <param name="e"></param>
   220         private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
   221         {
   222 			iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
   223         }
   224 
   225 
   226 		/// <summary>
   227 		/// Mute check box changed.
   228 		/// </summary>
   229 		/// <param name="sender"></param>
   230 		/// <param name="e"></param>
   231 		private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
   232 		{
   233 			iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
   234 		}
   235 
   236 
   237         /// <summary>
   238         /// Device State Changed
   239         /// </summary>
   240         public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
   241 
   242         /// <summary>
   243         /// Device Added
   244         /// </summary>
   245         public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
   246 
   247         /// <summary>
   248         /// Device Removed
   249         /// </summary>
   250         public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
   251 
   252         /// <summary>
   253         /// Default Device Changed
   254         /// </summary>
   255         public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
   256         {
   257             if (role == Role.Multimedia && flow == DataFlow.Render)
   258             {
   259                 UpdateAudioDeviceAndMasterVolumeThreadSafe();
   260             }
   261         }
   262 
   263         /// <summary>
   264         /// Property Value Changed
   265         /// </summary>
   266         /// <param name="pwstrDeviceId"></param>
   267         /// <param name="key"></param>
   268         public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
   269 
   270 
   271         
   272 
   273 		/// <summary>
   274 		/// 
   275 		/// </summary>
   276 		private void UpdateMasterVolumeThreadSafe()
   277 		{
   278 			if (this.InvokeRequired)
   279 			{
   280 				//Not in the proper thread, invoke ourselves
   281 				PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
   282 				this.Invoke(d, new object[] { });
   283 				return;
   284 			}
   285 
   286 			//Update volume slider
   287 			float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
   288 			trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
   289 			//Update mute checkbox
   290 			checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
   291 
   292 			//TODO: Check our display device too
   293 			if (iDisplay.IsOpen())
   294 			{
   295 				int volumeIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume);
   296 				if (volumeIconCount > 0)
   297 				{
   298 					int currentVolume = Convert.ToInt32(volumeLevelScalar * volumeIconCount);
   299 					for (int i = 0; i < volumeIconCount; i++)
   300 					{
   301 						if (i < currentVolume)
   302 						{
   303 							iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 10);
   304 						}
   305 						else
   306 						{
   307 							iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 0);
   308 						}
   309 					}
   310 				}
   311 
   312 				int muteIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconMute);
   313 				if (muteIconCount > 0)
   314 				{
   315 					for (int i = 0; i < muteIconCount; i++)
   316 					{
   317 						if (iMultiMediaDevice.AudioEndpointVolume.Mute)
   318 						{
   319 							iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconMute, i, 10);
   320 						}
   321 						else
   322 						{
   323 							iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconMute, i, 0);
   324 						}
   325 					}
   326 				}
   327 
   328 			}
   329 
   330 		}
   331 
   332         /// <summary>
   333         /// 
   334         /// </summary>
   335         private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
   336         {
   337             if (this.InvokeRequired)
   338             {
   339                 //Not in the proper thread, invoke ourselves
   340 				PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
   341                 this.Invoke(d, new object[] { });
   342                 return;
   343             }
   344             
   345             //We are in the correct thread just go ahead.
   346             try
   347             {                
   348                 //Get our master volume            
   349 				iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
   350                 //Show our volume in our track bar
   351 				UpdateMasterVolumeThreadSafe();
   352 
   353                 //Register to get volume modifications
   354 				iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
   355                 //
   356 				trackBarMasterVolume.Enabled = true;
   357             }
   358             catch (Exception ex)
   359             {
   360                 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
   361                 Debug.WriteLine(ex.ToString());
   362                 //Something went wrong S/PDIF device ca throw exception I guess
   363 				trackBarMasterVolume.Enabled = false;
   364             }
   365         }
   366 
   367 		/// <summary>
   368 		/// 
   369 		/// </summary>
   370 		private void PopulateDeviceTypes()
   371 		{
   372 			int count = Display.TypeCount();
   373 
   374 			for (int i = 0; i < count; i++)
   375 			{
   376 				comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
   377 			}
   378 		}
   379 
   380 		/// <summary>
   381 		///
   382 		/// </summary>
   383 		private void SetupTrayIcon()
   384 		{
   385 			iNotifyIcon.Icon = GetIcon("vfd.ico");
   386 			iNotifyIcon.Text = "Sharp Display Manager";
   387 			iNotifyIcon.Visible = true;
   388 
   389 			//Double click toggles visibility - typically brings up the application
   390 			iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
   391 			{
   392 				SysTrayHideShow();
   393 			};
   394 
   395 			//Adding a context menu, useful to be able to exit the application
   396 			ContextMenu contextMenu = new ContextMenu();
   397 			//Context menu item to toggle visibility
   398 			MenuItem hideShowItem = new MenuItem("Hide/Show");
   399 			hideShowItem.Click += delegate(object obj, EventArgs args)
   400 			{
   401 				SysTrayHideShow();
   402 			};
   403 			contextMenu.MenuItems.Add(hideShowItem);
   404 
   405 			//Context menu item separator
   406 			contextMenu.MenuItems.Add(new MenuItem("-"));
   407 
   408 			//Context menu exit item
   409 			MenuItem exitItem = new MenuItem("Exit");
   410 			exitItem.Click += delegate(object obj, EventArgs args)
   411 			{
   412 				Application.Exit();
   413 			};
   414 			contextMenu.MenuItems.Add(exitItem);
   415 
   416 			iNotifyIcon.ContextMenu = contextMenu;
   417 		}
   418 
   419 		/// <summary>
   420 		/// Access icons from embedded resources.
   421 		/// </summary>
   422 		/// <param name="name"></param>
   423 		/// <returns></returns>
   424 		public static Icon GetIcon(string name)
   425 		{
   426 			name = "SharpDisplayManager.Resources." + name;
   427 
   428 			string[] names =
   429 			  Assembly.GetExecutingAssembly().GetManifestResourceNames();
   430 			for (int i = 0; i < names.Length; i++)
   431 			{
   432 				if (names[i].Replace('\\', '.') == name)
   433 				{
   434 					using (Stream stream = Assembly.GetExecutingAssembly().
   435 					  GetManifestResourceStream(names[i]))
   436 					{
   437 						return new Icon(stream);
   438 					}
   439 				}
   440 			}
   441 
   442 			return null;
   443 		}
   444 
   445 
   446         /// <summary>
   447         /// Set our current client.
   448         /// This will take care of applying our client layout and set data fields.
   449         /// </summary>
   450         /// <param name="aSessionId"></param>
   451         void SetCurrentClient(string aSessionId)
   452         {
   453             if (aSessionId == iCurrentClientSessionId)
   454             {
   455                 //Given client is already the current one.
   456                 //Don't bother changing anything then.
   457                 return;
   458             }
   459 
   460             //Set current client ID.
   461             iCurrentClientSessionId = aSessionId;
   462             //Fetch and set current client data.
   463             iCurrentClientData = iClients[aSessionId];
   464             //Apply layout and set data fields.
   465             UpdateTableLayoutPanel(iCurrentClientData);
   466         }
   467 
   468         private void buttonFont_Click(object sender, EventArgs e)
   469         {
   470             //fontDialog.ShowColor = true;
   471             //fontDialog.ShowApply = true;
   472             fontDialog.ShowEffects = true;
   473             fontDialog.Font = cds.Font;
   474 
   475             fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
   476 
   477             //fontDialog.ShowHelp = true;
   478 
   479             //fontDlg.MaxSize = 40;
   480             //fontDlg.MinSize = 22;
   481 
   482             //fontDialog.Parent = this;
   483             //fontDialog.StartPosition = FormStartPosition.CenterParent;
   484 
   485             //DlgBox.ShowDialog(fontDialog);
   486 
   487             //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
   488             if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
   489             {
   490                 //Set the fonts to all our labels in our layout
   491                 foreach (Control ctrl in tableLayoutPanel.Controls)
   492                 {
   493                     if (ctrl is MarqueeLabel)
   494                     {
   495                         ((MarqueeLabel)ctrl).Font = fontDialog.Font;
   496                     }
   497                 }
   498 
   499                 //Save font settings
   500                 cds.Font = fontDialog.Font;
   501                 Properties.Settings.Default.Save();
   502                 //
   503                 CheckFontHeight();
   504             }
   505         }
   506 
   507         /// <summary>
   508         ///
   509         /// </summary>
   510         void CheckFontHeight()
   511         {
   512             //Show font height and width
   513             labelFontHeight.Text = "Font height: " + cds.Font.Height;
   514             float charWidth = IsFixedWidth(cds.Font);
   515             if (charWidth == 0.0f)
   516             {
   517                 labelFontWidth.Visible = false;
   518             }
   519             else
   520             {
   521                 labelFontWidth.Visible = true;
   522                 labelFontWidth.Text = "Font width: " + charWidth;
   523             }
   524 
   525             MarqueeLabel label = null;
   526             //Get the first label control we can find
   527             foreach (Control ctrl in tableLayoutPanel.Controls)
   528             {
   529                 if (ctrl is MarqueeLabel)
   530                 {
   531                     label = (MarqueeLabel)ctrl;
   532                     break;
   533                 }
   534             }
   535 
   536             //Now check font height and show a warning if needed.
   537             if (label != null && label.Font.Height > label.Height)
   538             {
   539                 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
   540                 labelWarning.Visible = true;
   541             }
   542             else
   543             {
   544                 labelWarning.Visible = false;
   545             }
   546 
   547         }
   548 
   549         private void buttonCapture_Click(object sender, EventArgs e)
   550         {
   551             System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
   552             tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
   553             //Bitmap bmpToSave = new Bitmap(bmp);
   554             bmp.Save("D:\\capture.png");
   555 
   556             ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
   557 
   558             /*
   559             string outputFileName = "d:\\capture.png";
   560             using (MemoryStream memory = new MemoryStream())
   561             {
   562                 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
   563                 {
   564                     bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
   565                     byte[] bytes = memory.ToArray();
   566                     fs.Write(bytes, 0, bytes.Length);
   567                 }
   568             }
   569              */
   570 
   571         }
   572 
   573         private void CheckForRequestResults()
   574         {
   575             if (iDisplay.IsRequestPending())
   576             {
   577                 switch (iDisplay.AttemptRequestCompletion())
   578                 {
   579                     case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
   580                         toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
   581                         //Issue next request then
   582                         iDisplay.RequestPowerSupplyStatus();
   583                         break;
   584 
   585                     case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
   586                         if (iDisplay.PowerSupplyStatus())
   587                         {
   588                             toolStripStatusLabelPower.Text = "ON";
   589                         }
   590                         else
   591                         {
   592                             toolStripStatusLabelPower.Text = "OFF";
   593                         }
   594                         //Issue next request then
   595                         iDisplay.RequestDeviceId();
   596                         break;
   597 
   598                     case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
   599                         toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
   600                         //No more request to issue
   601                         break;
   602                 }
   603             }
   604         }
   605 
   606         public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
   607         {
   608             if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
   609             {
   610                 return 0xFFFFFFFF;
   611             }
   612             return 0x00000000;
   613         }
   614 
   615         public static uint ColorUntouched(int aX, int aY, uint aPixel)
   616         {
   617             return aPixel;
   618         }
   619 
   620         public static uint ColorInversed(int aX, int aY, uint aPixel)
   621         {
   622             return ~aPixel;
   623         }
   624 
   625         public static uint ColorChessboard(int aX, int aY, uint aPixel)
   626         {
   627             if ((aX % 2 == 0) && (aY % 2 == 0))
   628             {
   629                 return ~aPixel;
   630             }
   631             else if ((aX % 2 != 0) && (aY % 2 != 0))
   632             {
   633                 return ~aPixel;
   634             }
   635             return 0x00000000;
   636         }
   637 
   638 
   639         public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
   640         {
   641             return aBmp.Width - aX - 1;
   642         }
   643 
   644         public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
   645         {
   646             return iBmp.Height - aY - 1;
   647         }
   648 
   649         public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
   650         {
   651             return aX;
   652         }
   653 
   654         public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
   655         {
   656             return aY;
   657         }
   658 
   659         /// <summary>
   660         /// Select proper pixel delegates according to our current settings.
   661         /// </summary>
   662         private void SetupPixelDelegates()
   663         {
   664             //Select our pixel processing routine
   665             if (cds.InverseColors)
   666             {
   667                 //iColorFx = ColorChessboard;
   668                 iColorFx = ColorInversed;
   669             }
   670             else
   671             {
   672                 iColorFx = ColorWhiteIsOn;
   673             }
   674 
   675             //Select proper coordinate translation functions
   676             //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
   677             if (cds.ReverseScreen)
   678             {
   679                 iScreenX = ScreenReversedX;
   680                 iScreenY = ScreenReversedY;
   681             }
   682             else
   683             {
   684                 iScreenX = ScreenX;
   685                 iScreenY = ScreenY;
   686             }
   687 
   688         }
   689 
   690         //This is our timer tick responsible to perform our render
   691         private void timer_Tick(object sender, EventArgs e)
   692         {
   693             //Update our animations
   694             DateTime NewTickTime = DateTime.Now;
   695 
   696             //Update animation for all our marquees
   697             foreach (Control ctrl in tableLayoutPanel.Controls)
   698             {
   699                 if (ctrl is MarqueeLabel)
   700                 {
   701                     ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
   702                 }
   703             }
   704 
   705 
   706             //Update our display
   707             if (iDisplay.IsOpen())
   708             {
   709                 CheckForRequestResults();
   710 
   711                 //Draw to bitmap
   712                 if (iCreateBitmap)
   713                 {
   714                     iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
   715                 }
   716                 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
   717                 //iBmp.Save("D:\\capture.png");
   718 
   719                 //Send it to our display
   720                 for (int i = 0; i < iBmp.Width; i++)
   721                 {
   722                     for (int j = 0; j < iBmp.Height; j++)
   723                     {
   724                         unchecked
   725                         {
   726                             //Get our processed pixel coordinates
   727                             int x = iScreenX(iBmp, i);
   728                             int y = iScreenY(iBmp, j);
   729                             //Get pixel color
   730                             uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
   731                             //Apply color effects
   732                             color = iColorFx(x,y,color);
   733                             //Now set our pixel
   734                             iDisplay.SetPixel(x, y, color);
   735                         }
   736                     }
   737                 }
   738 
   739                 iDisplay.SwapBuffers();
   740 
   741             }
   742 
   743             //Compute instant FPS
   744             toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
   745 
   746             LastTickTime = NewTickTime;
   747 
   748         }
   749 
   750 		/// <summary>
   751 		/// Attempt to establish connection with our display hardware.
   752 		/// </summary>
   753         private void OpenDisplayConnection()
   754         {
   755             CloseDisplayConnection();
   756 
   757             if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
   758             {   
   759 				UpdateStatus();               
   760 				toolStripStatusLabelConnect.Text = "Connection error";
   761             }
   762         }
   763 
   764         private void CloseDisplayConnection()
   765         {
   766 			//Status will be updated upon receiving the closed event
   767             iDisplay.Close();
   768         }
   769 
   770         private void buttonOpen_Click(object sender, EventArgs e)
   771         {
   772             OpenDisplayConnection();
   773         }
   774 
   775         private void buttonClose_Click(object sender, EventArgs e)
   776         {
   777             CloseDisplayConnection();
   778         }
   779 
   780         private void buttonClear_Click(object sender, EventArgs e)
   781         {
   782             iDisplay.Clear();
   783             iDisplay.SwapBuffers();
   784         }
   785 
   786         private void buttonFill_Click(object sender, EventArgs e)
   787         {
   788             iDisplay.Fill();
   789             iDisplay.SwapBuffers();
   790         }
   791 
   792         private void trackBarBrightness_Scroll(object sender, EventArgs e)
   793         {
   794             cds.Brightness = trackBarBrightness.Value;
   795             Properties.Settings.Default.Save();
   796             iDisplay.SetBrightness(trackBarBrightness.Value);
   797 
   798         }
   799 
   800 
   801         /// <summary>
   802         /// CDS stands for Current Display Settings
   803         /// </summary>
   804         private DisplaySettings cds
   805         {
   806             get
   807             {
   808                 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
   809                 if (settings == null)
   810                 {
   811                     settings = new DisplaysSettings();
   812                     settings.Init();
   813                     Properties.Settings.Default.DisplaysSettings = settings;
   814                 }
   815 
   816                 //Make sure all our settings have been created
   817                 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
   818                 {
   819                     settings.Displays.Add(new DisplaySettings());
   820                 }
   821 
   822                 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
   823                 return displaySettings;
   824             }
   825         }
   826 
   827         /// <summary>
   828         /// Check if the given font has a fixed character pitch.
   829         /// </summary>
   830         /// <param name="ft"></param>
   831         /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
   832         public float IsFixedWidth(Font ft)
   833         {
   834             Graphics g = CreateGraphics();
   835             char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
   836             float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
   837 
   838             bool fixedWidth = true;
   839 
   840             foreach (char c in charSizes)
   841                 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
   842                     fixedWidth = false;
   843 
   844             if (fixedWidth)
   845             {
   846                 return charWidth;
   847             }
   848 
   849             return 0.0f;
   850         }
   851 
   852 		/// <summary>
   853 		/// Synchronize UI with settings
   854 		/// </summary>
   855         private void UpdateStatus()
   856         {            
   857             //Load settings
   858             checkBoxShowBorders.Checked = cds.ShowBorders;
   859             tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
   860 
   861             //Set the proper font to each of our labels
   862             foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
   863             {
   864                 ctrl.Font = cds.Font;
   865             }
   866 
   867             CheckFontHeight();
   868 			//Check if "run on Windows startup" is enabled
   869 			checkBoxAutoStart.Checked = iStartupManager.Startup;
   870 			//
   871             checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
   872 			checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
   873 			checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
   874             checkBoxReverseScreen.Checked = cds.ReverseScreen;
   875             checkBoxInverseColors.Checked = cds.InverseColors;
   876             checkBoxScaleToFit.Checked = cds.ScaleToFit;
   877             maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
   878             labelMinFontSize.Enabled = cds.ScaleToFit;
   879             maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
   880 			maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
   881             comboBoxDisplayType.SelectedIndex = cds.DisplayType;
   882             timer.Interval = cds.TimerInterval;
   883             maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
   884             textBoxScrollLoopSeparator.Text = cds.Separator;
   885             //
   886             SetupPixelDelegates();
   887 
   888             if (iDisplay.IsOpen())
   889             {
   890 				//We have a display connection
   891 				//Reflect that in our UI
   892 
   893 				tableLayoutPanel.Enabled = true;
   894 				panelDisplay.Enabled = true;
   895 
   896                 //Only setup brightness if display is open
   897                 trackBarBrightness.Minimum = iDisplay.MinBrightness();
   898                 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
   899 				if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
   900 				{
   901 					//Brightness out of range, this can occur when using auto-detect
   902 					//Use max brightness instead
   903 					trackBarBrightness.Value = iDisplay.MaxBrightness();
   904 					iDisplay.SetBrightness(iDisplay.MaxBrightness());
   905 				}
   906 				else
   907 				{
   908 					trackBarBrightness.Value = cds.Brightness;
   909 					iDisplay.SetBrightness(cds.Brightness);
   910 				}
   911 
   912 				//Try compute the steps to something that makes sense
   913                 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
   914                 trackBarBrightness.SmallChange = 1;
   915                 
   916                 //
   917                 buttonFill.Enabled = true;
   918                 buttonClear.Enabled = true;
   919                 buttonOpen.Enabled = false;
   920                 buttonClose.Enabled = true;
   921                 trackBarBrightness.Enabled = true;
   922                 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
   923                 //+ " - " + iDisplay.SerialNumber();
   924 
   925                 if (iDisplay.SupportPowerOnOff())
   926                 {
   927                     buttonPowerOn.Enabled = true;
   928                     buttonPowerOff.Enabled = true;
   929                 }
   930                 else
   931                 {
   932                     buttonPowerOn.Enabled = false;
   933                     buttonPowerOff.Enabled = false;
   934                 }
   935 
   936                 if (iDisplay.SupportClock())
   937                 {
   938                     buttonShowClock.Enabled = true;
   939                     buttonHideClock.Enabled = true;
   940                 }
   941                 else
   942                 {
   943                     buttonShowClock.Enabled = false;
   944                     buttonHideClock.Enabled = false;
   945                 }
   946             }
   947             else
   948             {
   949 				//Display is connection not available
   950 				//Reflect that in our UI
   951 				tableLayoutPanel.Enabled = false;
   952 				panelDisplay.Enabled = false;
   953                 buttonFill.Enabled = false;
   954                 buttonClear.Enabled = false;
   955                 buttonOpen.Enabled = true;
   956                 buttonClose.Enabled = false;
   957                 trackBarBrightness.Enabled = false;
   958                 buttonPowerOn.Enabled = false;
   959                 buttonPowerOff.Enabled = false;
   960                 buttonShowClock.Enabled = false;
   961                 buttonHideClock.Enabled = false;
   962                 toolStripStatusLabelConnect.Text = "Disconnected";
   963                 toolStripStatusLabelPower.Text = "N/A";
   964             }
   965 
   966         }
   967 
   968 
   969 
   970         private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
   971         {
   972             //Save our show borders setting
   973             tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
   974             cds.ShowBorders = checkBoxShowBorders.Checked;
   975             Properties.Settings.Default.Save();
   976             CheckFontHeight();
   977         }
   978 
   979         private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
   980         {
   981             //Save our connect on startup setting
   982             Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
   983             Properties.Settings.Default.Save();
   984         }
   985 
   986 		private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
   987 		{
   988 			//Save our "Minimize to tray" setting
   989 			Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
   990 			Properties.Settings.Default.Save();
   991 
   992 		}
   993 
   994 		private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
   995 		{
   996 			//Save our "Start minimized" setting
   997 			Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
   998 			Properties.Settings.Default.Save();
   999 		}
  1000 
  1001 		private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
  1002 		{
  1003 			iStartupManager.Startup = checkBoxAutoStart.Checked;
  1004 		}
  1005 
  1006 
  1007         private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
  1008         {
  1009             //Save our reverse screen setting
  1010             cds.ReverseScreen = checkBoxReverseScreen.Checked;
  1011             Properties.Settings.Default.Save();
  1012             SetupPixelDelegates();
  1013         }
  1014 
  1015         private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
  1016         {
  1017             //Save our inverse colors setting
  1018             cds.InverseColors = checkBoxInverseColors.Checked;
  1019             Properties.Settings.Default.Save();
  1020             SetupPixelDelegates();
  1021         }
  1022 
  1023         private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
  1024         {
  1025             //Save our scale to fit setting
  1026             cds.ScaleToFit = checkBoxScaleToFit.Checked;
  1027             Properties.Settings.Default.Save();
  1028             //
  1029             labelMinFontSize.Enabled = cds.ScaleToFit;
  1030             maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
  1031         }
  1032 
  1033         private void MainForm_Resize(object sender, EventArgs e)
  1034         {
  1035             if (WindowState == FormWindowState.Minimized)
  1036             {
  1037                 // Do some stuff
  1038                 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
  1039                 iCreateBitmap = true;
  1040             }
  1041         }
  1042 
  1043         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
  1044         {
  1045 			CloseDisplayConnection();
  1046             StopServer();
  1047             e.Cancel = iClosing;
  1048         }
  1049 
  1050         public void StartServer()
  1051         {
  1052             iServiceHost = new ServiceHost
  1053                 (
  1054                     typeof(Session),
  1055                     new Uri[] { new Uri("net.tcp://localhost:8001/") }
  1056                 );
  1057 
  1058             iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
  1059             iServiceHost.Open();
  1060         }
  1061 
  1062         public void StopServer()
  1063         {
  1064             if (iClients.Count > 0 && !iClosing)
  1065             {
  1066                 //Tell our clients
  1067                 iClosing = true;
  1068                 BroadcastCloseEvent();
  1069             }
  1070             else if (iClosing)
  1071             {
  1072                 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
  1073                 {
  1074                     iClosing = false; //We make sure we force close if asked twice
  1075                 }
  1076             }
  1077             else
  1078             {
  1079                 //We removed that as it often lags for some reason
  1080                 //iServiceHost.Close();
  1081             }
  1082         }
  1083 
  1084         public void BroadcastCloseEvent()
  1085         {
  1086             Trace.TraceInformation("BroadcastCloseEvent - start");
  1087 
  1088             var inactiveClients = new List<string>();
  1089             foreach (var client in iClients)
  1090             {
  1091                 //if (client.Key != eventData.ClientName)
  1092                 {
  1093                     try
  1094                     {
  1095                         Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
  1096                         client.Value.Callback.OnCloseOrder(/*eventData*/);
  1097                     }
  1098                     catch (Exception ex)
  1099                     {
  1100                         inactiveClients.Add(client.Key);
  1101                     }
  1102                 }
  1103             }
  1104 
  1105             if (inactiveClients.Count > 0)
  1106             {
  1107                 foreach (var client in inactiveClients)
  1108                 {
  1109                     iClients.Remove(client);
  1110                     Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
  1111                 }
  1112             }
  1113 
  1114 			if (iClients.Count==0)
  1115 			{
  1116 				ClearLayout();
  1117 			}
  1118         }
  1119 
  1120 		/// <summary>
  1121 		/// Just remove all our fields.
  1122 		/// </summary>
  1123 		private void ClearLayout()
  1124 		{
  1125 			tableLayoutPanel.Controls.Clear();
  1126 			tableLayoutPanel.RowStyles.Clear();
  1127 			tableLayoutPanel.ColumnStyles.Clear();
  1128 		}
  1129 
  1130 		/// <summary>
  1131 		/// Just launch a demo client.
  1132 		/// </summary>
  1133 		private void StartNewClient(string aTopText = "", string aBottomText = "")
  1134 		{
  1135 			Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
  1136 			SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
  1137 			clientThread.Start(myParams);
  1138 			BringToFront();
  1139 		}
  1140 
  1141         private void buttonStartClient_Click(object sender, EventArgs e)
  1142         {
  1143 			StartNewClient();
  1144         }
  1145 
  1146         private void buttonSuspend_Click(object sender, EventArgs e)
  1147         {
  1148             LastTickTime = DateTime.Now; //Reset timer to prevent jump
  1149             timer.Enabled = !timer.Enabled;
  1150             if (!timer.Enabled)
  1151             {
  1152                 buttonSuspend.Text = "Run";
  1153             }
  1154             else
  1155             {
  1156                 buttonSuspend.Text = "Pause";
  1157             }
  1158         }
  1159 
  1160         private void buttonCloseClients_Click(object sender, EventArgs e)
  1161         {
  1162             BroadcastCloseEvent();
  1163         }
  1164 
  1165         private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
  1166         {
  1167 
  1168         }
  1169 
  1170 
  1171         /// <summary>
  1172         ///
  1173         /// </summary>
  1174         /// <param name="aSessionId"></param>
  1175         /// <param name="aCallback"></param>
  1176         public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
  1177         {
  1178             if (this.InvokeRequired)
  1179             {
  1180                 //Not in the proper thread, invoke ourselves
  1181                 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
  1182                 this.Invoke(d, new object[] { aSessionId, aCallback });
  1183             }
  1184             else
  1185             {
  1186                 //We are in the proper thread
  1187                 //Add this session to our collection of clients
  1188                 ClientData newClient = new ClientData(aSessionId, aCallback);
  1189                 Program.iMainForm.iClients.Add(aSessionId, newClient);
  1190                 //Add this session to our UI
  1191                 UpdateClientTreeViewNode(newClient);
  1192             }
  1193         }
  1194 
  1195         /// <summary>
  1196         ///
  1197         /// </summary>
  1198         /// <param name="aSessionId"></param>
  1199         public void RemoveClientThreadSafe(string aSessionId)
  1200         {
  1201             if (this.InvokeRequired)
  1202             {
  1203                 //Not in the proper thread, invoke ourselves
  1204                 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
  1205                 this.Invoke(d, new object[] { aSessionId });
  1206             }
  1207             else
  1208             {
  1209                 //We are in the proper thread
  1210                 //Remove this session from both client collection and UI tree view
  1211                 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
  1212                 {
  1213                     Program.iMainForm.iClients.Remove(aSessionId);
  1214                     Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
  1215                 }
  1216 
  1217 				if (iClients.Count == 0)
  1218 				{
  1219 					//Clear our screen when last client disconnects
  1220 					ClearLayout();
  1221 
  1222 					if (iClosing)
  1223 					{
  1224 						//We were closing our form
  1225 						//All clients are now closed
  1226 						//Just resume our close operation
  1227 						iClosing = false;
  1228 						Close();
  1229 					}
  1230 				}
  1231             }
  1232         }
  1233 
  1234         /// <summary>
  1235         ///
  1236         /// </summary>
  1237         /// <param name="aSessionId"></param>
  1238         /// <param name="aLayout"></param>
  1239         public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
  1240         {
  1241             if (this.InvokeRequired)
  1242             {
  1243                 //Not in the proper thread, invoke ourselves
  1244                 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
  1245                 this.Invoke(d, new object[] { aSessionId, aLayout });
  1246             }
  1247             else
  1248             {
  1249                 ClientData client = iClients[aSessionId];
  1250                 if (client != null)
  1251                 {
  1252                     client.Layout = aLayout;
  1253                     UpdateTableLayoutPanel(client);
  1254                     //
  1255                     UpdateClientTreeViewNode(client);
  1256                 }
  1257             }
  1258         }
  1259 
  1260         /// <summary>
  1261         ///
  1262         /// </summary>
  1263         /// <param name="aSessionId"></param>
  1264         /// <param name="aField"></param>
  1265         public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
  1266         {
  1267             if (this.InvokeRequired)
  1268             {
  1269                 //Not in the proper thread, invoke ourselves
  1270                 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
  1271                 this.Invoke(d, new object[] { aSessionId, aField });
  1272             }
  1273             else
  1274             {
  1275                 //We are in the proper thread
  1276                 //Call the non-thread-safe variant
  1277                 SetClientField(aSessionId, aField);
  1278             }
  1279         }
  1280 
  1281         /// <summary>
  1282         ///
  1283         /// </summary>
  1284         /// <param name="aSessionId"></param>
  1285         /// <param name="aField"></param>
  1286         private void SetClientField(string aSessionId, DataField aField)
  1287         {
  1288             SetCurrentClient(aSessionId);
  1289             ClientData client = iClients[aSessionId];
  1290             if (client != null)
  1291             {
  1292                 bool somethingChanged = false;
  1293 
  1294                 //Make sure all our fields are in place
  1295                 while (client.Fields.Count < (aField.Index + 1))
  1296                 {
  1297                     //Add a text field with proper index
  1298                     client.Fields.Add(new DataField(client.Fields.Count));
  1299                     somethingChanged = true;
  1300                 }
  1301 
  1302                 if (client.Fields[aField.Index].IsSameLayout(aField))
  1303                 {
  1304                     //Same layout just update our field
  1305                     client.Fields[aField.Index] = aField;
  1306                     //
  1307                     if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
  1308                     {
  1309                         //Text field control already in place, just change the text
  1310                         MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
  1311                         somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
  1312                         label.Text = aField.Text;
  1313                         label.TextAlign = aField.Alignment;
  1314                     }
  1315                     else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
  1316                     {
  1317                         somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
  1318                         //Bitmap field control already in place just change the bitmap
  1319                         PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
  1320                         pictureBox.Image = aField.Bitmap;
  1321                     }
  1322                     else
  1323                     {
  1324                         somethingChanged = true;
  1325                         //The requested control in our layout it not of the correct type
  1326                         //Wrong control type, re-create them all
  1327                         UpdateTableLayoutPanel(iCurrentClientData);
  1328                     }
  1329                 }
  1330                 else
  1331                 {
  1332                     somethingChanged = true;
  1333                     //Different layout, need to rebuild it
  1334                     client.Fields[aField.Index] = aField;
  1335                     UpdateTableLayoutPanel(iCurrentClientData);
  1336                 }
  1337 
  1338                 //
  1339                 if (somethingChanged)
  1340                 {
  1341                     UpdateClientTreeViewNode(client);
  1342                 }
  1343             }
  1344         }
  1345 
  1346         /// <summary>
  1347         ///
  1348         /// </summary>
  1349         /// <param name="aTexts"></param>
  1350         public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
  1351         {
  1352             if (this.InvokeRequired)
  1353             {
  1354                 //Not in the proper thread, invoke ourselves
  1355                 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
  1356                 this.Invoke(d, new object[] { aSessionId, aFields });
  1357             }
  1358             else
  1359             {
  1360                 //Put each our text fields in a label control
  1361                 foreach (DataField field in aFields)
  1362                 {
  1363                     SetClientField(aSessionId, field);
  1364                 }
  1365             }
  1366         }
  1367 
  1368         /// <summary>
  1369         ///
  1370         /// </summary>
  1371         /// <param name="aSessionId"></param>
  1372         /// <param name="aName"></param>
  1373         public void SetClientNameThreadSafe(string aSessionId, string aName)
  1374         {
  1375             if (this.InvokeRequired)
  1376             {
  1377                 //Not in the proper thread, invoke ourselves
  1378                 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
  1379                 this.Invoke(d, new object[] { aSessionId, aName });
  1380             }
  1381             else
  1382             {
  1383                 //We are in the proper thread
  1384                 //Get our client
  1385                 ClientData client = iClients[aSessionId];
  1386                 if (client != null)
  1387                 {
  1388                     //Set its name
  1389                     client.Name = aName;
  1390                     //Update our tree-view
  1391                     UpdateClientTreeViewNode(client);
  1392                 }
  1393             }
  1394         }
  1395 
  1396         /// <summary>
  1397         ///
  1398         /// </summary>
  1399         /// <param name="aClient"></param>
  1400         private void UpdateClientTreeViewNode(ClientData aClient)
  1401         {
  1402             if (aClient == null)
  1403             {
  1404                 return;
  1405             }
  1406 
  1407             TreeNode node = null;
  1408             //Check that our client node already exists
  1409             //Get our client root node using its key which is our session ID
  1410             TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
  1411             if (nodes.Count()>0)
  1412             {
  1413                 //We already have a node for that client
  1414                 node = nodes[0];
  1415                 //Clear children as we are going to recreate them below
  1416                 node.Nodes.Clear();
  1417             }
  1418             else
  1419             {
  1420                 //Client node does not exists create a new one
  1421                 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
  1422                 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
  1423             }
  1424 
  1425             if (node != null)
  1426             {
  1427                 //Change its name
  1428                 if (aClient.Name != "")
  1429                 {
  1430                     //We have a name, us it as text for our root node
  1431                     node.Text = aClient.Name;
  1432                     //Add a child with SessionId
  1433                     node.Nodes.Add(new TreeNode(aClient.SessionId));
  1434                 }
  1435                 else
  1436                 {
  1437                     //No name, use session ID instead
  1438                     node.Text = aClient.SessionId;
  1439                 }
  1440 
  1441                 if (aClient.Fields.Count > 0)
  1442                 {
  1443                     //Create root node for our texts
  1444                     TreeNode textsRoot = new TreeNode("Fields");
  1445                     node.Nodes.Add(textsRoot);
  1446                     //For each text add a new entry
  1447                     foreach (DataField field in aClient.Fields)
  1448                     {
  1449                         if (!field.IsBitmap)
  1450                         {
  1451                             DataField textField = (DataField)field;
  1452                             textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
  1453                         }
  1454                         else
  1455                         {
  1456                             textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
  1457                         }
  1458                     }
  1459                 }
  1460 
  1461                 node.ExpandAll();
  1462             }
  1463         }
  1464 
  1465         /// <summary>
  1466         /// Update our table layout row styles to make sure each rows have similar height
  1467         /// </summary>
  1468         private void UpdateTableLayoutRowStyles()
  1469         {
  1470             foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
  1471             {
  1472                 rowStyle.SizeType = SizeType.Percent;
  1473                 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
  1474             }
  1475         }
  1476 
  1477         /// DEPRECATED
  1478         /// <summary>
  1479         /// Empty and recreate our table layout with the given number of columns and rows.
  1480         /// Sizes of rows and columns are uniform.
  1481         /// </summary>
  1482         /// <param name="aColumn"></param>
  1483         /// <param name="aRow"></param>
  1484         private void UpdateTableLayoutPanel(int aColumn, int aRow)
  1485         {
  1486             tableLayoutPanel.Controls.Clear();
  1487             tableLayoutPanel.RowStyles.Clear();
  1488             tableLayoutPanel.ColumnStyles.Clear();
  1489             tableLayoutPanel.RowCount = 0;
  1490             tableLayoutPanel.ColumnCount = 0;
  1491 
  1492             while (tableLayoutPanel.RowCount < aRow)
  1493             {
  1494                 tableLayoutPanel.RowCount++;
  1495             }
  1496 
  1497             while (tableLayoutPanel.ColumnCount < aColumn)
  1498             {
  1499                 tableLayoutPanel.ColumnCount++;
  1500             }
  1501 
  1502             for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
  1503             {
  1504                 //Create our column styles
  1505                 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
  1506 
  1507                 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
  1508                 {
  1509                     if (i == 0)
  1510                     {
  1511                         //Create our row styles
  1512                         this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
  1513                     }
  1514 
  1515                     MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
  1516                     control.AutoEllipsis = true;
  1517                     control.AutoSize = true;
  1518                     control.BackColor = System.Drawing.Color.Transparent;
  1519                     control.Dock = System.Windows.Forms.DockStyle.Fill;
  1520                     control.Location = new System.Drawing.Point(1, 1);
  1521                     control.Margin = new System.Windows.Forms.Padding(0);
  1522                     control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
  1523                     control.OwnTimer = false;
  1524                     control.PixelsPerSecond = 64;
  1525                     control.Separator = cds.Separator;
  1526                     control.MinFontSize = cds.MinFontSize;
  1527                     control.ScaleToFit = cds.ScaleToFit;
  1528                     //control.Size = new System.Drawing.Size(254, 30);
  1529                     //control.TabIndex = 2;
  1530                     control.Font = cds.Font;
  1531                     control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
  1532                     control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
  1533                     control.UseCompatibleTextRendering = true;
  1534                     //
  1535                     tableLayoutPanel.Controls.Add(control, i, j);
  1536                 }
  1537             }
  1538 
  1539             CheckFontHeight();
  1540         }
  1541 
  1542 
  1543         /// <summary>
  1544         /// Update our display table layout.
  1545         /// </summary>
  1546         /// <param name="aLayout"></param>
  1547         private void UpdateTableLayoutPanel(ClientData aClient)
  1548         {
  1549 			if (aClient == null)
  1550 			{
  1551 				//Just drop it
  1552 				return;
  1553 			}
  1554 
  1555 
  1556             TableLayout layout = aClient.Layout;
  1557             int fieldCount = 0;
  1558 
  1559             tableLayoutPanel.Controls.Clear();
  1560             tableLayoutPanel.RowStyles.Clear();
  1561             tableLayoutPanel.ColumnStyles.Clear();
  1562             tableLayoutPanel.RowCount = 0;
  1563             tableLayoutPanel.ColumnCount = 0;
  1564 
  1565             while (tableLayoutPanel.RowCount < layout.Rows.Count)
  1566             {
  1567                 tableLayoutPanel.RowCount++;
  1568             }
  1569 
  1570             while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
  1571             {
  1572                 tableLayoutPanel.ColumnCount++;
  1573             }
  1574 
  1575             for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
  1576             {
  1577                 //Create our column styles
  1578                 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
  1579 
  1580                 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
  1581                 {
  1582                     if (i == 0)
  1583                     {
  1584                         //Create our row styles
  1585                         this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
  1586                     }
  1587 
  1588                     //Check if we already have a control
  1589                     Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
  1590                     if (existingControl!=null)
  1591                     {
  1592                         //We already have a control in that cell as a results of row/col spanning
  1593                         //Move on to next cell then
  1594                         continue;
  1595                     }
  1596 
  1597                     fieldCount++;
  1598 
  1599                     //Check if a client field already exists for that cell
  1600                     if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
  1601                     {
  1602                         //No client field specified, create a text field by default
  1603                         aClient.Fields.Add(new DataField(aClient.Fields.Count));
  1604                     }
  1605 
  1606                     //Create a control corresponding to the field specified for that cell
  1607                     DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
  1608                     Control control = CreateControlForDataField(field);
  1609 
  1610                     //Add newly created control to our table layout at the specified row and column
  1611                     tableLayoutPanel.Controls.Add(control, i, j);
  1612                     //Make sure we specify row and column span for that new control
  1613                     tableLayoutPanel.SetRowSpan(control,field.RowSpan);
  1614                     tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
  1615                 }
  1616             }
  1617 
  1618             //
  1619             while (aClient.Fields.Count > fieldCount)
  1620             {
  1621                 //We have too much fields for this layout
  1622                 //Just discard them until we get there
  1623                 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
  1624             }
  1625 
  1626             CheckFontHeight();
  1627         }
  1628 
  1629         /// <summary>
  1630         /// Check our type of data field and create corresponding control
  1631         /// </summary>
  1632         /// <param name="aField"></param>
  1633         private Control CreateControlForDataField(DataField aField)
  1634         {
  1635             Control control=null;
  1636             if (!aField.IsBitmap)
  1637             {
  1638                 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
  1639                 label.AutoEllipsis = true;
  1640                 label.AutoSize = true;
  1641                 label.BackColor = System.Drawing.Color.Transparent;
  1642                 label.Dock = System.Windows.Forms.DockStyle.Fill;
  1643                 label.Location = new System.Drawing.Point(1, 1);
  1644                 label.Margin = new System.Windows.Forms.Padding(0);
  1645                 label.Name = "marqueeLabel" + aField.Index;
  1646                 label.OwnTimer = false;
  1647                 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
  1648                 label.Separator = cds.Separator;
  1649                 label.MinFontSize = cds.MinFontSize;
  1650                 label.ScaleToFit = cds.ScaleToFit;
  1651                 //control.Size = new System.Drawing.Size(254, 30);
  1652                 //control.TabIndex = 2;
  1653                 label.Font = cds.Font;
  1654 
  1655 				label.TextAlign = aField.Alignment;
  1656                 label.UseCompatibleTextRendering = true;
  1657                 label.Text = aField.Text;
  1658                 //
  1659                 control = label;
  1660             }
  1661             else
  1662             {
  1663                 //Create picture box
  1664                 PictureBox picture = new PictureBox();
  1665                 picture.AutoSize = true;
  1666                 picture.BackColor = System.Drawing.Color.Transparent;
  1667                 picture.Dock = System.Windows.Forms.DockStyle.Fill;
  1668                 picture.Location = new System.Drawing.Point(1, 1);
  1669                 picture.Margin = new System.Windows.Forms.Padding(0);
  1670                 picture.Name = "pictureBox" + aField;
  1671                 //Set our image
  1672                 picture.Image = aField.Bitmap;
  1673                 //
  1674                 control = picture;
  1675             }
  1676 
  1677             return control;
  1678         }
  1679 
  1680 		/// <summary>
  1681 		/// Called when the user selected a new display type.
  1682 		/// </summary>
  1683 		/// <param name="sender"></param>
  1684 		/// <param name="e"></param>
  1685         private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
  1686         {
  1687 			//Store the selected display type in our settings
  1688             Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
  1689             cds.DisplayType = comboBoxDisplayType.SelectedIndex;
  1690             Properties.Settings.Default.Save();
  1691 
  1692 			//Try re-opening the display connection if we were already connected.
  1693 			//Otherwise just update our status to reflect display type change.
  1694             if (iDisplay.IsOpen())
  1695             {
  1696                 OpenDisplayConnection();
  1697             }
  1698             else
  1699             {
  1700                 UpdateStatus();
  1701             }
  1702         }
  1703 
  1704         private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
  1705         {
  1706             if (maskedTextBoxTimerInterval.Text != "")
  1707             {
  1708                 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
  1709 
  1710                 if (interval > 0)
  1711                 {
  1712                     timer.Interval = interval;
  1713                     cds.TimerInterval = timer.Interval;
  1714                     Properties.Settings.Default.Save();
  1715                 }
  1716             }
  1717         }
  1718 
  1719         private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
  1720         {
  1721             if (maskedTextBoxMinFontSize.Text != "")
  1722             {
  1723                 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
  1724 
  1725                 if (minFontSize > 0)
  1726                 {
  1727                     cds.MinFontSize = minFontSize;
  1728                     Properties.Settings.Default.Save();
  1729 					//We need to recreate our layout for that change to take effect
  1730 					UpdateTableLayoutPanel(iCurrentClientData);
  1731                 }
  1732             }
  1733         }
  1734 
  1735 
  1736 		private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
  1737 		{
  1738 			if (maskedTextBoxScrollingSpeed.Text != "")
  1739 			{
  1740 				int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
  1741 
  1742 				if (scrollingSpeed > 0)
  1743 				{
  1744 					cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
  1745 					Properties.Settings.Default.Save();
  1746 					//We need to recreate our layout for that change to take effect
  1747 					UpdateTableLayoutPanel(iCurrentClientData);
  1748 				}
  1749 			}
  1750 		}
  1751 
  1752         private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
  1753         {
  1754             cds.Separator = textBoxScrollLoopSeparator.Text;
  1755             Properties.Settings.Default.Save();
  1756 
  1757 			//Update our text fields
  1758 			foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
  1759 			{
  1760 				ctrl.Separator = cds.Separator;
  1761 			}
  1762 
  1763         }
  1764 
  1765         private void buttonPowerOn_Click(object sender, EventArgs e)
  1766         {
  1767             iDisplay.PowerOn();
  1768         }
  1769 
  1770         private void buttonPowerOff_Click(object sender, EventArgs e)
  1771         {
  1772             iDisplay.PowerOff();
  1773         }
  1774 
  1775         private void buttonShowClock_Click(object sender, EventArgs e)
  1776         {
  1777             iDisplay.ShowClock();
  1778         }
  1779 
  1780         private void buttonHideClock_Click(object sender, EventArgs e)
  1781         {
  1782             iDisplay.HideClock();
  1783         }
  1784 
  1785         private void buttonUpdate_Click(object sender, EventArgs e)
  1786         {
  1787             InstallUpdateSyncWithInfo();
  1788         }
  1789 
  1790 
  1791         private void InstallUpdateSyncWithInfo()
  1792         {
  1793             UpdateCheckInfo info = null;
  1794 
  1795             if (ApplicationDeployment.IsNetworkDeployed)
  1796             {
  1797                 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
  1798 
  1799                 try
  1800                 {
  1801                     info = ad.CheckForDetailedUpdate();
  1802 
  1803                 }
  1804                 catch (DeploymentDownloadException dde)
  1805                 {
  1806                     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);
  1807                     return;
  1808                 }
  1809                 catch (InvalidDeploymentException ide)
  1810                 {
  1811                     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);
  1812                     return;
  1813                 }
  1814                 catch (InvalidOperationException ioe)
  1815                 {
  1816                     MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
  1817                     return;
  1818                 }
  1819 
  1820 				if (info.UpdateAvailable)
  1821 				{
  1822 					Boolean doUpdate = true;
  1823 
  1824 					if (!info.IsUpdateRequired)
  1825 					{
  1826 						DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
  1827 						if (!(DialogResult.OK == dr))
  1828 						{
  1829 							doUpdate = false;
  1830 						}
  1831 					}
  1832 					else
  1833 					{
  1834 						// Display a message that the app MUST reboot. Display the minimum required version.
  1835 						MessageBox.Show("This application has detected a mandatory update from your current " +
  1836 							"version to version " + info.MinimumRequiredVersion.ToString() +
  1837 							". The application will now install the update and restart.",
  1838 							"Update Available", MessageBoxButtons.OK,
  1839 							MessageBoxIcon.Information);
  1840 					}
  1841 
  1842 					if (doUpdate)
  1843 					{
  1844 						try
  1845 						{
  1846 							ad.Update();
  1847 							MessageBox.Show("The application has been upgraded, and will now restart.");
  1848 							Application.Restart();
  1849 						}
  1850 						catch (DeploymentDownloadException dde)
  1851 						{
  1852 							MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
  1853 							return;
  1854 						}
  1855 					}
  1856 				}
  1857 				else
  1858 				{
  1859 					MessageBox.Show("You are already running the latest version.", "Application up-to-date");
  1860 				}
  1861             }
  1862         }
  1863 
  1864 
  1865 		/// <summary>
  1866 		/// Used to
  1867 		/// </summary>
  1868 		private void SysTrayHideShow()
  1869 		{
  1870 			Visible = !Visible;
  1871 			if (Visible)
  1872 			{
  1873 				Activate();
  1874 				WindowState = FormWindowState.Normal;
  1875 			}
  1876 		}
  1877 
  1878 		/// <summary>
  1879 		/// Use to handle minimize events.
  1880 		/// </summary>
  1881 		/// <param name="sender"></param>
  1882 		/// <param name="e"></param>
  1883 		private void MainForm_SizeChanged(object sender, EventArgs e)
  1884 		{
  1885 			if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
  1886 			{
  1887 				if (Visible)
  1888 				{
  1889 					SysTrayHideShow();
  1890 				}
  1891 			}
  1892 
  1893 		}
  1894 
  1895 		/// <summary>
  1896 		/// 
  1897 		/// </summary>
  1898 		/// <param name="sender"></param>
  1899 		/// <param name="e"></param>
  1900 		private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
  1901 		{
  1902 			//Our table layout size has changed which means our display size has changed.
  1903 			//We need to re-create our bitmap.
  1904 			iCreateBitmap = true;
  1905 		}
  1906 
  1907     }
  1908 
  1909     /// <summary>
  1910     /// A UI thread copy of a client relevant data.
  1911     /// Keeping this copy in the UI thread helps us deal with threading issues.
  1912     /// </summary>
  1913     public class ClientData
  1914     {
  1915         public ClientData(string aSessionId, ICallback aCallback)
  1916         {
  1917             SessionId = aSessionId;
  1918             Name = "";
  1919             Fields = new List<DataField>();
  1920             Layout = new TableLayout(1, 2); //Default to one column and two rows
  1921             Callback = aCallback;
  1922         }
  1923 
  1924         public string SessionId { get; set; }
  1925         public string Name { get; set; }
  1926         public List<DataField> Fields { get; set; }
  1927         public TableLayout Layout { get; set; }
  1928         public ICallback Callback { get; set; }
  1929     }
  1930 }