Fixing div by zero in network signal animation code of GP1212 displays.
2 using System.Collections.Generic;
3 using System.ComponentModel;
8 using System.Threading.Tasks;
9 using System.Windows.Forms;
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;
19 using NAudio.CoreAudioApi;
20 using NAudio.CoreAudioApi.Interfaces;
21 using System.Runtime.InteropServices;
25 using SharpDisplayClient;
28 namespace SharpDisplayManager
31 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
32 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
33 //Delegates are used for our thread safe method
34 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
35 public delegate void RemoveClientDelegate(string aSessionId);
36 public delegate void SetFieldDelegate(string SessionId, DataField aField);
37 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
38 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
39 public delegate void SetClientNameDelegate(string aSessionId, string aName);
40 public delegate void PlainUpdateDelegate();
44 /// Our Display manager main form
46 public partial class MainForm : Form, IMMNotificationClient
48 DateTime LastTickTime;
50 System.Drawing.Bitmap iBmp;
51 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
52 ServiceHost iServiceHost;
53 // Our collection of clients sorted by session id.
54 public Dictionary<string, ClientData> iClients;
55 // The name of the client which informations are currently displayed.
56 public string iCurrentClientSessionId;
57 ClientData iCurrentClientData;
60 //Function pointer for pixel color filtering
61 ColorProcessingDelegate iColorFx;
62 //Function pointer for pixel X coordinate intercept
63 CoordinateTranslationDelegate iScreenX;
64 //Function pointer for pixel Y coordinate intercept
65 CoordinateTranslationDelegate iScreenY;
67 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
68 private MMDevice iMultiMediaDevice;
70 private NetworkManager iNetworkManager;
74 /// Manage run when Windows startup option
76 private StartupManager iStartupManager;
81 private NotifyIconAdv iNotifyIcon;
85 iCurrentClientSessionId = "";
86 iCurrentClientData = null;
87 LastTickTime = DateTime.Now;
88 //Instantiate our display and register for events notifications
89 iDisplay = new Display();
90 iDisplay.OnOpened += OnDisplayOpened;
91 iDisplay.OnClosed += OnDisplayClosed;
93 iClients = new Dictionary<string, ClientData>();
94 iStartupManager = new StartupManager();
95 iNotifyIcon = new NotifyIconAdv();
97 //Have our designer initialize its controls
98 InitializeComponent();
100 //Populate device types
101 PopulateDeviceTypes();
103 //Initial status update
106 //We have a bug when drawing minimized and reusing our bitmap
107 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
108 iCreateBitmap = false;
110 //Minimize our window if desired
111 if (Properties.Settings.Default.StartMinimized)
113 WindowState = FormWindowState.Minimized;
121 /// <param name="sender"></param>
122 /// <param name="e"></param>
123 private void MainForm_Load(object sender, EventArgs e)
125 //Check if we are running a Click Once deployed application
126 if (ApplicationDeployment.IsNetworkDeployed)
128 //This is a proper Click Once installation, fetch and show our version number
129 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
133 //Not a proper Click Once installation, assuming development build then
134 this.Text += " - development";
138 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
139 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
140 UpdateAudioDeviceAndMasterVolumeThreadSafe();
143 iNetworkManager = new NetworkManager();
144 iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
145 UpdateNetworkStatus();
147 //Setup notification icon
150 // To make sure start up with minimize to tray works
151 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
157 //When not debugging we want the screen to be empty until a client takes over
160 //When developing we want at least one client for testing
161 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
164 //Open display connection on start-up if needed
165 if (Properties.Settings.Default.DisplayConnectOnStartup)
167 OpenDisplayConnection();
170 //Start our server so that we can get client requests
175 /// Called when our display is opened.
177 /// <param name="aDisplay"></param>
178 private void OnDisplayOpened(Display aDisplay)
180 //Set our screen size now that our display is connected
181 //Our panelDisplay is the container of our tableLayoutPanel
182 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
183 //panelDisplay needs an extra 2 pixels for borders on each sides
184 //tableLayoutPanel will eventually be the exact size of our display
185 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
186 panelDisplay.Size = size;
188 //Our display was just opened, update our UI
190 //Initiate asynchronous request
191 iDisplay.RequestFirmwareRevision();
194 UpdateMasterVolumeThreadSafe();
196 UpdateNetworkStatus();
199 //Testing icon in debug, no arm done if icon not supported
200 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
201 //iDisplay.SetAllIconsStatus(2);
207 /// Called when our display is closed.
209 /// <param name="aDisplay"></param>
210 private void OnDisplayClosed(Display aDisplay)
212 //Our display was just closed, update our UI consequently
216 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
218 //Update network status
219 UpdateNetworkStatus();
223 /// Update our Network Status
225 private void UpdateNetworkStatus()
227 if (iDisplay.IsOpen())
229 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconInternet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
230 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
235 int iLastNetworkIconIndex = 0;
236 int iUpdateCountSinceLastNetworkAnimation = 0;
241 private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
243 iUpdateCountSinceLastNetworkAnimation++;
244 iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
246 if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
248 int iconCount=iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal);
251 //Prevents div by zero and other undefined behavior
254 iLastNetworkIconIndex++;
255 iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2);
256 for (int i=0;i<iconCount;i++)
258 if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && !(i == 1 && iLastNetworkIconIndex > 4))
260 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
264 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
273 /// Receive volume change notification and reflect changes on our slider.
275 /// <param name="data"></param>
276 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
278 UpdateMasterVolumeThreadSafe();
282 /// Update master volume when user moves our slider.
284 /// <param name="sender"></param>
285 /// <param name="e"></param>
286 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
288 //Just like Windows Volume Mixer we unmute if the volume is adjusted
289 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
290 //Set volume level according to our volume slider new position
291 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
296 /// Mute check box changed.
298 /// <param name="sender"></param>
299 /// <param name="e"></param>
300 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
302 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
306 /// Device State Changed
308 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
313 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
318 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
321 /// Default Device Changed
323 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
325 if (role == Role.Multimedia && flow == DataFlow.Render)
327 UpdateAudioDeviceAndMasterVolumeThreadSafe();
332 /// Property Value Changed
334 /// <param name="pwstrDeviceId"></param>
335 /// <param name="key"></param>
336 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
342 /// Update master volume indicators based our current system states.
343 /// This typically includes volume levels and mute status.
345 private void UpdateMasterVolumeThreadSafe()
347 if (this.InvokeRequired)
349 //Not in the proper thread, invoke ourselves
350 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
351 this.Invoke(d, new object[] { });
355 //Update volume slider
356 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
357 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
358 //Update mute checkbox
359 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
361 //If our display connection is open we need to update its icons
362 if (iDisplay.IsOpen())
364 //First take care our our volume level icons
365 int volumeIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume);
366 if (volumeIconCount > 0)
368 //Compute current volume level from system level and the number of segments in our display volume bar.
369 //That tells us how many segments in our volume bar needs to be turned on.
370 float currentVolume = volumeLevelScalar * volumeIconCount;
371 int segmentOnCount = Convert.ToInt32(currentVolume);
372 //Check if our segment count was rounded up, this will later be used for half brightness segment
373 bool roundedUp = segmentOnCount > currentVolume;
375 for (int i = 0; i < volumeIconCount; i++)
377 if (i < segmentOnCount)
379 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
380 if (i == segmentOnCount - 1 && roundedUp)
383 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, (iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1)/2);
388 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1);
393 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 0);
398 //Take care our our mute icon
399 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconMute, iMultiMediaDevice.AudioEndpointVolume.Mute);
407 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
409 if (this.InvokeRequired)
411 //Not in the proper thread, invoke ourselves
412 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
413 this.Invoke(d, new object[] { });
417 //We are in the correct thread just go ahead.
420 //Get our master volume
421 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
423 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
425 //Show our volume in our track bar
426 UpdateMasterVolumeThreadSafe();
428 //Register to get volume modifications
429 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
431 trackBarMasterVolume.Enabled = true;
435 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
436 Debug.WriteLine(ex.ToString());
437 //Something went wrong S/PDIF device ca throw exception I guess
438 trackBarMasterVolume.Enabled = false;
445 private void PopulateDeviceTypes()
447 int count = Display.TypeCount();
449 for (int i = 0; i < count; i++)
451 comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
458 private void SetupTrayIcon()
460 iNotifyIcon.Icon = GetIcon("vfd.ico");
461 iNotifyIcon.Text = "Sharp Display Manager";
462 iNotifyIcon.Visible = true;
464 //Double click toggles visibility - typically brings up the application
465 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
470 //Adding a context menu, useful to be able to exit the application
471 ContextMenu contextMenu = new ContextMenu();
472 //Context menu item to toggle visibility
473 MenuItem hideShowItem = new MenuItem("Hide/Show");
474 hideShowItem.Click += delegate(object obj, EventArgs args)
478 contextMenu.MenuItems.Add(hideShowItem);
480 //Context menu item separator
481 contextMenu.MenuItems.Add(new MenuItem("-"));
483 //Context menu exit item
484 MenuItem exitItem = new MenuItem("Exit");
485 exitItem.Click += delegate(object obj, EventArgs args)
489 contextMenu.MenuItems.Add(exitItem);
491 iNotifyIcon.ContextMenu = contextMenu;
495 /// Access icons from embedded resources.
497 /// <param name="name"></param>
498 /// <returns></returns>
499 public static Icon GetIcon(string name)
501 name = "SharpDisplayManager.Resources." + name;
504 Assembly.GetExecutingAssembly().GetManifestResourceNames();
505 for (int i = 0; i < names.Length; i++)
507 if (names[i].Replace('\\', '.') == name)
509 using (Stream stream = Assembly.GetExecutingAssembly().
510 GetManifestResourceStream(names[i]))
512 return new Icon(stream);
522 /// Set our current client.
523 /// This will take care of applying our client layout and set data fields.
525 /// <param name="aSessionId"></param>
526 void SetCurrentClient(string aSessionId)
528 if (aSessionId == iCurrentClientSessionId)
530 //Given client is already the current one.
531 //Don't bother changing anything then.
535 //Set current client ID.
536 iCurrentClientSessionId = aSessionId;
537 //Fetch and set current client data.
538 iCurrentClientData = iClients[aSessionId];
539 //Apply layout and set data fields.
540 UpdateTableLayoutPanel(iCurrentClientData);
543 private void buttonFont_Click(object sender, EventArgs e)
545 //fontDialog.ShowColor = true;
546 //fontDialog.ShowApply = true;
547 fontDialog.ShowEffects = true;
548 fontDialog.Font = cds.Font;
550 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
552 //fontDialog.ShowHelp = true;
554 //fontDlg.MaxSize = 40;
555 //fontDlg.MinSize = 22;
557 //fontDialog.Parent = this;
558 //fontDialog.StartPosition = FormStartPosition.CenterParent;
560 //DlgBox.ShowDialog(fontDialog);
562 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
563 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
565 //Set the fonts to all our labels in our layout
566 foreach (Control ctrl in tableLayoutPanel.Controls)
568 if (ctrl is MarqueeLabel)
570 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
575 cds.Font = fontDialog.Font;
576 Properties.Settings.Default.Save();
585 void CheckFontHeight()
587 //Show font height and width
588 labelFontHeight.Text = "Font height: " + cds.Font.Height;
589 float charWidth = IsFixedWidth(cds.Font);
590 if (charWidth == 0.0f)
592 labelFontWidth.Visible = false;
596 labelFontWidth.Visible = true;
597 labelFontWidth.Text = "Font width: " + charWidth;
600 MarqueeLabel label = null;
601 //Get the first label control we can find
602 foreach (Control ctrl in tableLayoutPanel.Controls)
604 if (ctrl is MarqueeLabel)
606 label = (MarqueeLabel)ctrl;
611 //Now check font height and show a warning if needed.
612 if (label != null && label.Font.Height > label.Height)
614 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
615 labelWarning.Visible = true;
619 labelWarning.Visible = false;
624 private void buttonCapture_Click(object sender, EventArgs e)
626 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
627 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
628 //Bitmap bmpToSave = new Bitmap(bmp);
629 bmp.Save("D:\\capture.png");
631 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
634 string outputFileName = "d:\\capture.png";
635 using (MemoryStream memory = new MemoryStream())
637 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
639 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
640 byte[] bytes = memory.ToArray();
641 fs.Write(bytes, 0, bytes.Length);
648 private void CheckForRequestResults()
650 if (iDisplay.IsRequestPending())
652 switch (iDisplay.AttemptRequestCompletion())
654 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
655 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
656 //Issue next request then
657 iDisplay.RequestPowerSupplyStatus();
660 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
661 if (iDisplay.PowerSupplyStatus())
663 toolStripStatusLabelPower.Text = "ON";
667 toolStripStatusLabelPower.Text = "OFF";
669 //Issue next request then
670 iDisplay.RequestDeviceId();
673 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
674 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
675 //No more request to issue
681 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
683 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
690 public static uint ColorUntouched(int aX, int aY, uint aPixel)
695 public static uint ColorInversed(int aX, int aY, uint aPixel)
700 public static uint ColorChessboard(int aX, int aY, uint aPixel)
702 if ((aX % 2 == 0) && (aY % 2 == 0))
706 else if ((aX % 2 != 0) && (aY % 2 != 0))
714 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
716 return aBmp.Width - aX - 1;
719 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
721 return iBmp.Height - aY - 1;
724 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
729 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
735 /// Select proper pixel delegates according to our current settings.
737 private void SetupPixelDelegates()
739 //Select our pixel processing routine
740 if (cds.InverseColors)
742 //iColorFx = ColorChessboard;
743 iColorFx = ColorInversed;
747 iColorFx = ColorWhiteIsOn;
750 //Select proper coordinate translation functions
751 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
752 if (cds.ReverseScreen)
754 iScreenX = ScreenReversedX;
755 iScreenY = ScreenReversedY;
765 //This is our timer tick responsible to perform our render
766 private void timer_Tick(object sender, EventArgs e)
768 //Update our animations
769 DateTime NewTickTime = DateTime.Now;
771 UpdateNetworkSignal(LastTickTime, NewTickTime);
773 //Update animation for all our marquees
774 foreach (Control ctrl in tableLayoutPanel.Controls)
776 if (ctrl is MarqueeLabel)
778 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
784 if (iDisplay.IsOpen())
786 CheckForRequestResults();
791 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
793 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
794 //iBmp.Save("D:\\capture.png");
796 //Send it to our display
797 for (int i = 0; i < iBmp.Width; i++)
799 for (int j = 0; j < iBmp.Height; j++)
803 //Get our processed pixel coordinates
804 int x = iScreenX(iBmp, i);
805 int y = iScreenY(iBmp, j);
807 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
808 //Apply color effects
809 color = iColorFx(x,y,color);
811 iDisplay.SetPixel(x, y, color);
816 iDisplay.SwapBuffers();
820 //Compute instant FPS
821 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
823 LastTickTime = NewTickTime;
828 /// Attempt to establish connection with our display hardware.
830 private void OpenDisplayConnection()
832 CloseDisplayConnection();
834 if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
837 toolStripStatusLabelConnect.Text = "Connection error";
841 private void CloseDisplayConnection()
843 //Status will be updated upon receiving the closed event
847 private void buttonOpen_Click(object sender, EventArgs e)
849 OpenDisplayConnection();
852 private void buttonClose_Click(object sender, EventArgs e)
854 CloseDisplayConnection();
857 private void buttonClear_Click(object sender, EventArgs e)
860 iDisplay.SwapBuffers();
863 private void buttonFill_Click(object sender, EventArgs e)
866 iDisplay.SwapBuffers();
869 private void trackBarBrightness_Scroll(object sender, EventArgs e)
871 cds.Brightness = trackBarBrightness.Value;
872 Properties.Settings.Default.Save();
873 iDisplay.SetBrightness(trackBarBrightness.Value);
879 /// CDS stands for Current Display Settings
881 private DisplaySettings cds
885 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
886 if (settings == null)
888 settings = new DisplaysSettings();
890 Properties.Settings.Default.DisplaysSettings = settings;
893 //Make sure all our settings have been created
894 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
896 settings.Displays.Add(new DisplaySettings());
899 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
900 return displaySettings;
905 /// Check if the given font has a fixed character pitch.
907 /// <param name="ft"></param>
908 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
909 public float IsFixedWidth(Font ft)
911 Graphics g = CreateGraphics();
912 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
913 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
915 bool fixedWidth = true;
917 foreach (char c in charSizes)
918 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
930 /// Synchronize UI with settings
932 private void UpdateStatus()
935 checkBoxShowBorders.Checked = cds.ShowBorders;
936 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
938 //Set the proper font to each of our labels
939 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
941 ctrl.Font = cds.Font;
945 //Check if "run on Windows startup" is enabled
946 checkBoxAutoStart.Checked = iStartupManager.Startup;
948 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
949 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
950 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
951 checkBoxReverseScreen.Checked = cds.ReverseScreen;
952 checkBoxInverseColors.Checked = cds.InverseColors;
953 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
954 checkBoxScaleToFit.Checked = cds.ScaleToFit;
955 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
956 labelMinFontSize.Enabled = cds.ScaleToFit;
957 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
958 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
959 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
960 timer.Interval = cds.TimerInterval;
961 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
962 textBoxScrollLoopSeparator.Text = cds.Separator;
964 SetupPixelDelegates();
966 if (iDisplay.IsOpen())
968 //We have a display connection
969 //Reflect that in our UI
971 tableLayoutPanel.Enabled = true;
972 panelDisplay.Enabled = true;
974 //Only setup brightness if display is open
975 trackBarBrightness.Minimum = iDisplay.MinBrightness();
976 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
977 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
979 //Brightness out of range, this can occur when using auto-detect
980 //Use max brightness instead
981 trackBarBrightness.Value = iDisplay.MaxBrightness();
982 iDisplay.SetBrightness(iDisplay.MaxBrightness());
986 trackBarBrightness.Value = cds.Brightness;
987 iDisplay.SetBrightness(cds.Brightness);
990 //Try compute the steps to something that makes sense
991 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
992 trackBarBrightness.SmallChange = 1;
995 buttonFill.Enabled = true;
996 buttonClear.Enabled = true;
997 buttonOpen.Enabled = false;
998 buttonClose.Enabled = true;
999 trackBarBrightness.Enabled = true;
1000 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1001 //+ " - " + iDisplay.SerialNumber();
1003 if (iDisplay.SupportPowerOnOff())
1005 buttonPowerOn.Enabled = true;
1006 buttonPowerOff.Enabled = true;
1010 buttonPowerOn.Enabled = false;
1011 buttonPowerOff.Enabled = false;
1014 if (iDisplay.SupportClock())
1016 buttonShowClock.Enabled = true;
1017 buttonHideClock.Enabled = true;
1021 buttonShowClock.Enabled = false;
1022 buttonHideClock.Enabled = false;
1026 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1027 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel)>0;
1029 if (cds.ShowVolumeLabel)
1031 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1035 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1040 //Display is connection not available
1041 //Reflect that in our UI
1042 checkBoxShowVolumeLabel.Enabled = false;
1043 tableLayoutPanel.Enabled = false;
1044 panelDisplay.Enabled = false;
1045 buttonFill.Enabled = false;
1046 buttonClear.Enabled = false;
1047 buttonOpen.Enabled = true;
1048 buttonClose.Enabled = false;
1049 trackBarBrightness.Enabled = false;
1050 buttonPowerOn.Enabled = false;
1051 buttonPowerOff.Enabled = false;
1052 buttonShowClock.Enabled = false;
1053 buttonHideClock.Enabled = false;
1054 toolStripStatusLabelConnect.Text = "Disconnected";
1055 toolStripStatusLabelPower.Text = "N/A";
1064 /// <param name="sender"></param>
1065 /// <param name="e"></param>
1066 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1068 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1069 Properties.Settings.Default.Save();
1073 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1075 //Save our show borders setting
1076 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1077 cds.ShowBorders = checkBoxShowBorders.Checked;
1078 Properties.Settings.Default.Save();
1082 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1084 //Save our connect on startup setting
1085 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1086 Properties.Settings.Default.Save();
1089 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1091 //Save our "Minimize to tray" setting
1092 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1093 Properties.Settings.Default.Save();
1097 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1099 //Save our "Start minimized" setting
1100 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1101 Properties.Settings.Default.Save();
1104 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1106 iStartupManager.Startup = checkBoxAutoStart.Checked;
1110 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1112 //Save our reverse screen setting
1113 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1114 Properties.Settings.Default.Save();
1115 SetupPixelDelegates();
1118 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1120 //Save our inverse colors setting
1121 cds.InverseColors = checkBoxInverseColors.Checked;
1122 Properties.Settings.Default.Save();
1123 SetupPixelDelegates();
1126 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1128 //Save our scale to fit setting
1129 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1130 Properties.Settings.Default.Save();
1132 labelMinFontSize.Enabled = cds.ScaleToFit;
1133 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1136 private void MainForm_Resize(object sender, EventArgs e)
1138 if (WindowState == FormWindowState.Minimized)
1141 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1142 iCreateBitmap = true;
1146 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1148 iNetworkManager.Dispose();
1149 CloseDisplayConnection();
1151 e.Cancel = iClosing;
1154 public void StartServer()
1156 iServiceHost = new ServiceHost
1159 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1162 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1163 iServiceHost.Open();
1166 public void StopServer()
1168 if (iClients.Count > 0 && !iClosing)
1172 BroadcastCloseEvent();
1176 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1178 iClosing = false; //We make sure we force close if asked twice
1183 //We removed that as it often lags for some reason
1184 //iServiceHost.Close();
1188 public void BroadcastCloseEvent()
1190 Trace.TraceInformation("BroadcastCloseEvent - start");
1192 var inactiveClients = new List<string>();
1193 foreach (var client in iClients)
1195 //if (client.Key != eventData.ClientName)
1199 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1200 client.Value.Callback.OnCloseOrder(/*eventData*/);
1202 catch (Exception ex)
1204 inactiveClients.Add(client.Key);
1209 if (inactiveClients.Count > 0)
1211 foreach (var client in inactiveClients)
1213 iClients.Remove(client);
1214 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1218 if (iClients.Count==0)
1225 /// Just remove all our fields.
1227 private void ClearLayout()
1229 tableLayoutPanel.Controls.Clear();
1230 tableLayoutPanel.RowStyles.Clear();
1231 tableLayoutPanel.ColumnStyles.Clear();
1235 /// Just launch a demo client.
1237 private void StartNewClient(string aTopText = "", string aBottomText = "")
1239 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1240 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1241 clientThread.Start(myParams);
1245 private void buttonStartClient_Click(object sender, EventArgs e)
1250 private void buttonSuspend_Click(object sender, EventArgs e)
1252 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1253 timer.Enabled = !timer.Enabled;
1256 buttonSuspend.Text = "Run";
1260 buttonSuspend.Text = "Pause";
1264 private void buttonCloseClients_Click(object sender, EventArgs e)
1266 BroadcastCloseEvent();
1269 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1278 /// <param name="aSessionId"></param>
1279 /// <param name="aCallback"></param>
1280 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1282 if (this.InvokeRequired)
1284 //Not in the proper thread, invoke ourselves
1285 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1286 this.Invoke(d, new object[] { aSessionId, aCallback });
1290 //We are in the proper thread
1291 //Add this session to our collection of clients
1292 ClientData newClient = new ClientData(aSessionId, aCallback);
1293 Program.iMainForm.iClients.Add(aSessionId, newClient);
1294 //Add this session to our UI
1295 UpdateClientTreeViewNode(newClient);
1302 /// <param name="aSessionId"></param>
1303 public void RemoveClientThreadSafe(string aSessionId)
1305 if (this.InvokeRequired)
1307 //Not in the proper thread, invoke ourselves
1308 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1309 this.Invoke(d, new object[] { aSessionId });
1313 //We are in the proper thread
1314 //Remove this session from both client collection and UI tree view
1315 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1317 Program.iMainForm.iClients.Remove(aSessionId);
1318 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1321 if (iClients.Count == 0)
1323 //Clear our screen when last client disconnects
1328 //We were closing our form
1329 //All clients are now closed
1330 //Just resume our close operation
1341 /// <param name="aSessionId"></param>
1342 /// <param name="aLayout"></param>
1343 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1345 if (this.InvokeRequired)
1347 //Not in the proper thread, invoke ourselves
1348 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1349 this.Invoke(d, new object[] { aSessionId, aLayout });
1353 ClientData client = iClients[aSessionId];
1356 client.Layout = aLayout;
1357 UpdateTableLayoutPanel(client);
1359 UpdateClientTreeViewNode(client);
1367 /// <param name="aSessionId"></param>
1368 /// <param name="aField"></param>
1369 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1371 if (this.InvokeRequired)
1373 //Not in the proper thread, invoke ourselves
1374 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1375 this.Invoke(d, new object[] { aSessionId, aField });
1379 //We are in the proper thread
1380 //Call the non-thread-safe variant
1381 SetClientField(aSessionId, aField);
1388 /// <param name="aSessionId"></param>
1389 /// <param name="aField"></param>
1390 private void SetClientField(string aSessionId, DataField aField)
1392 SetCurrentClient(aSessionId);
1393 ClientData client = iClients[aSessionId];
1396 bool somethingChanged = false;
1398 //Make sure all our fields are in place
1399 while (client.Fields.Count < (aField.Index + 1))
1401 //Add a text field with proper index
1402 client.Fields.Add(new DataField(client.Fields.Count));
1403 somethingChanged = true;
1406 if (client.Fields[aField.Index].IsSameLayout(aField))
1408 //Same layout just update our field
1409 client.Fields[aField.Index] = aField;
1411 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1413 //Text field control already in place, just change the text
1414 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1415 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1416 label.Text = aField.Text;
1417 label.TextAlign = aField.Alignment;
1419 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1421 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1422 //Bitmap field control already in place just change the bitmap
1423 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1424 pictureBox.Image = aField.Bitmap;
1428 somethingChanged = true;
1429 //The requested control in our layout it not of the correct type
1430 //Wrong control type, re-create them all
1431 UpdateTableLayoutPanel(iCurrentClientData);
1436 somethingChanged = true;
1437 //Different layout, need to rebuild it
1438 client.Fields[aField.Index] = aField;
1439 UpdateTableLayoutPanel(iCurrentClientData);
1443 if (somethingChanged)
1445 UpdateClientTreeViewNode(client);
1453 /// <param name="aTexts"></param>
1454 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1456 if (this.InvokeRequired)
1458 //Not in the proper thread, invoke ourselves
1459 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1460 this.Invoke(d, new object[] { aSessionId, aFields });
1464 //Put each our text fields in a label control
1465 foreach (DataField field in aFields)
1467 SetClientField(aSessionId, field);
1475 /// <param name="aSessionId"></param>
1476 /// <param name="aName"></param>
1477 public void SetClientNameThreadSafe(string aSessionId, string aName)
1479 if (this.InvokeRequired)
1481 //Not in the proper thread, invoke ourselves
1482 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1483 this.Invoke(d, new object[] { aSessionId, aName });
1487 //We are in the proper thread
1489 ClientData client = iClients[aSessionId];
1493 client.Name = aName;
1494 //Update our tree-view
1495 UpdateClientTreeViewNode(client);
1503 /// <param name="aClient"></param>
1504 private void UpdateClientTreeViewNode(ClientData aClient)
1506 if (aClient == null)
1511 TreeNode node = null;
1512 //Check that our client node already exists
1513 //Get our client root node using its key which is our session ID
1514 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1515 if (nodes.Count()>0)
1517 //We already have a node for that client
1519 //Clear children as we are going to recreate them below
1524 //Client node does not exists create a new one
1525 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1526 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1532 if (aClient.Name != "")
1534 //We have a name, us it as text for our root node
1535 node.Text = aClient.Name;
1536 //Add a child with SessionId
1537 node.Nodes.Add(new TreeNode(aClient.SessionId));
1541 //No name, use session ID instead
1542 node.Text = aClient.SessionId;
1545 if (aClient.Fields.Count > 0)
1547 //Create root node for our texts
1548 TreeNode textsRoot = new TreeNode("Fields");
1549 node.Nodes.Add(textsRoot);
1550 //For each text add a new entry
1551 foreach (DataField field in aClient.Fields)
1553 if (!field.IsBitmap)
1555 DataField textField = (DataField)field;
1556 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1560 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1570 /// Update our table layout row styles to make sure each rows have similar height
1572 private void UpdateTableLayoutRowStyles()
1574 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1576 rowStyle.SizeType = SizeType.Percent;
1577 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1583 /// Empty and recreate our table layout with the given number of columns and rows.
1584 /// Sizes of rows and columns are uniform.
1586 /// <param name="aColumn"></param>
1587 /// <param name="aRow"></param>
1588 private void UpdateTableLayoutPanel(int aColumn, int aRow)
1590 tableLayoutPanel.Controls.Clear();
1591 tableLayoutPanel.RowStyles.Clear();
1592 tableLayoutPanel.ColumnStyles.Clear();
1593 tableLayoutPanel.RowCount = 0;
1594 tableLayoutPanel.ColumnCount = 0;
1596 while (tableLayoutPanel.RowCount < aRow)
1598 tableLayoutPanel.RowCount++;
1601 while (tableLayoutPanel.ColumnCount < aColumn)
1603 tableLayoutPanel.ColumnCount++;
1606 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1608 //Create our column styles
1609 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
1611 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1615 //Create our row styles
1616 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
1619 MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
1620 control.AutoEllipsis = true;
1621 control.AutoSize = true;
1622 control.BackColor = System.Drawing.Color.Transparent;
1623 control.Dock = System.Windows.Forms.DockStyle.Fill;
1624 control.Location = new System.Drawing.Point(1, 1);
1625 control.Margin = new System.Windows.Forms.Padding(0);
1626 control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
1627 control.OwnTimer = false;
1628 control.PixelsPerSecond = 64;
1629 control.Separator = cds.Separator;
1630 control.MinFontSize = cds.MinFontSize;
1631 control.ScaleToFit = cds.ScaleToFit;
1632 //control.Size = new System.Drawing.Size(254, 30);
1633 //control.TabIndex = 2;
1634 control.Font = cds.Font;
1635 control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
1636 control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1637 control.UseCompatibleTextRendering = true;
1639 tableLayoutPanel.Controls.Add(control, i, j);
1648 /// Update our display table layout.
1650 /// <param name="aLayout"></param>
1651 private void UpdateTableLayoutPanel(ClientData aClient)
1653 if (aClient == null)
1660 TableLayout layout = aClient.Layout;
1663 tableLayoutPanel.Controls.Clear();
1664 tableLayoutPanel.RowStyles.Clear();
1665 tableLayoutPanel.ColumnStyles.Clear();
1666 tableLayoutPanel.RowCount = 0;
1667 tableLayoutPanel.ColumnCount = 0;
1669 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1671 tableLayoutPanel.RowCount++;
1674 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1676 tableLayoutPanel.ColumnCount++;
1679 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1681 //Create our column styles
1682 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1684 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1688 //Create our row styles
1689 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1692 //Check if we already have a control
1693 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1694 if (existingControl!=null)
1696 //We already have a control in that cell as a results of row/col spanning
1697 //Move on to next cell then
1703 //Check if a client field already exists for that cell
1704 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1706 //No client field specified, create a text field by default
1707 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1710 //Create a control corresponding to the field specified for that cell
1711 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1712 Control control = CreateControlForDataField(field);
1714 //Add newly created control to our table layout at the specified row and column
1715 tableLayoutPanel.Controls.Add(control, i, j);
1716 //Make sure we specify row and column span for that new control
1717 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1718 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1723 while (aClient.Fields.Count > fieldCount)
1725 //We have too much fields for this layout
1726 //Just discard them until we get there
1727 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1734 /// Check our type of data field and create corresponding control
1736 /// <param name="aField"></param>
1737 private Control CreateControlForDataField(DataField aField)
1739 Control control=null;
1740 if (!aField.IsBitmap)
1742 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1743 label.AutoEllipsis = true;
1744 label.AutoSize = true;
1745 label.BackColor = System.Drawing.Color.Transparent;
1746 label.Dock = System.Windows.Forms.DockStyle.Fill;
1747 label.Location = new System.Drawing.Point(1, 1);
1748 label.Margin = new System.Windows.Forms.Padding(0);
1749 label.Name = "marqueeLabel" + aField.Index;
1750 label.OwnTimer = false;
1751 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1752 label.Separator = cds.Separator;
1753 label.MinFontSize = cds.MinFontSize;
1754 label.ScaleToFit = cds.ScaleToFit;
1755 //control.Size = new System.Drawing.Size(254, 30);
1756 //control.TabIndex = 2;
1757 label.Font = cds.Font;
1759 label.TextAlign = aField.Alignment;
1760 label.UseCompatibleTextRendering = true;
1761 label.Text = aField.Text;
1767 //Create picture box
1768 PictureBox picture = new PictureBox();
1769 picture.AutoSize = true;
1770 picture.BackColor = System.Drawing.Color.Transparent;
1771 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1772 picture.Location = new System.Drawing.Point(1, 1);
1773 picture.Margin = new System.Windows.Forms.Padding(0);
1774 picture.Name = "pictureBox" + aField;
1776 picture.Image = aField.Bitmap;
1785 /// Called when the user selected a new display type.
1787 /// <param name="sender"></param>
1788 /// <param name="e"></param>
1789 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1791 //Store the selected display type in our settings
1792 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1793 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1794 Properties.Settings.Default.Save();
1796 //Try re-opening the display connection if we were already connected.
1797 //Otherwise just update our status to reflect display type change.
1798 if (iDisplay.IsOpen())
1800 OpenDisplayConnection();
1808 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1810 if (maskedTextBoxTimerInterval.Text != "")
1812 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1816 timer.Interval = interval;
1817 cds.TimerInterval = timer.Interval;
1818 Properties.Settings.Default.Save();
1823 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1825 if (maskedTextBoxMinFontSize.Text != "")
1827 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1829 if (minFontSize > 0)
1831 cds.MinFontSize = minFontSize;
1832 Properties.Settings.Default.Save();
1833 //We need to recreate our layout for that change to take effect
1834 UpdateTableLayoutPanel(iCurrentClientData);
1840 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1842 if (maskedTextBoxScrollingSpeed.Text != "")
1844 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1846 if (scrollingSpeed > 0)
1848 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1849 Properties.Settings.Default.Save();
1850 //We need to recreate our layout for that change to take effect
1851 UpdateTableLayoutPanel(iCurrentClientData);
1856 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1858 cds.Separator = textBoxScrollLoopSeparator.Text;
1859 Properties.Settings.Default.Save();
1861 //Update our text fields
1862 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1864 ctrl.Separator = cds.Separator;
1869 private void buttonPowerOn_Click(object sender, EventArgs e)
1874 private void buttonPowerOff_Click(object sender, EventArgs e)
1876 iDisplay.PowerOff();
1879 private void buttonShowClock_Click(object sender, EventArgs e)
1881 iDisplay.ShowClock();
1884 private void buttonHideClock_Click(object sender, EventArgs e)
1886 iDisplay.HideClock();
1889 private void buttonUpdate_Click(object sender, EventArgs e)
1891 InstallUpdateSyncWithInfo();
1895 private void InstallUpdateSyncWithInfo()
1897 UpdateCheckInfo info = null;
1899 if (ApplicationDeployment.IsNetworkDeployed)
1901 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1905 info = ad.CheckForDetailedUpdate();
1908 catch (DeploymentDownloadException dde)
1910 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);
1913 catch (InvalidDeploymentException ide)
1915 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);
1918 catch (InvalidOperationException ioe)
1920 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1924 if (info.UpdateAvailable)
1926 Boolean doUpdate = true;
1928 if (!info.IsUpdateRequired)
1930 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1931 if (!(DialogResult.OK == dr))
1938 // Display a message that the app MUST reboot. Display the minimum required version.
1939 MessageBox.Show("This application has detected a mandatory update from your current " +
1940 "version to version " + info.MinimumRequiredVersion.ToString() +
1941 ". The application will now install the update and restart.",
1942 "Update Available", MessageBoxButtons.OK,
1943 MessageBoxIcon.Information);
1951 MessageBox.Show("The application has been upgraded, and will now restart.");
1952 Application.Restart();
1954 catch (DeploymentDownloadException dde)
1956 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1963 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1972 private void SysTrayHideShow()
1978 WindowState = FormWindowState.Normal;
1983 /// Use to handle minimize events.
1985 /// <param name="sender"></param>
1986 /// <param name="e"></param>
1987 private void MainForm_SizeChanged(object sender, EventArgs e)
1989 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2002 /// <param name="sender"></param>
2003 /// <param name="e"></param>
2004 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2006 //Our table layout size has changed which means our display size has changed.
2007 //We need to re-create our bitmap.
2008 iCreateBitmap = true;
2013 /// A UI thread copy of a client relevant data.
2014 /// Keeping this copy in the UI thread helps us deal with threading issues.
2016 public class ClientData
2018 public ClientData(string aSessionId, ICallback aCallback)
2020 SessionId = aSessionId;
2022 Fields = new List<DataField>();
2023 Layout = new TableLayout(1, 2); //Default to one column and two rows
2024 Callback = aCallback;
2027 public string SessionId { get; set; }
2028 public string Name { get; set; }
2029 public List<DataField> Fields { get; set; }
2030 public TableLayout Layout { get; set; }
2031 public ICallback Callback { get; set; }