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