More consistent clock and clear support.
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;
61 public bool iSkipFrameRendering;
62 //Function pointer for pixel color filtering
63 ColorProcessingDelegate iColorFx;
64 //Function pointer for pixel X coordinate intercept
65 CoordinateTranslationDelegate iScreenX;
66 //Function pointer for pixel Y coordinate intercept
67 CoordinateTranslationDelegate iScreenY;
69 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
70 private MMDevice iMultiMediaDevice;
72 private NetworkManager iNetworkManager;
76 /// Manage run when Windows startup option
78 private StartupManager iStartupManager;
83 private NotifyIconAdv iNotifyIcon;
87 iSkipFrameRendering = false;
89 iCurrentClientSessionId = "";
90 iCurrentClientData = null;
91 LastTickTime = DateTime.Now;
92 //Instantiate our display and register for events notifications
93 iDisplay = new Display();
94 iDisplay.OnOpened += OnDisplayOpened;
95 iDisplay.OnClosed += OnDisplayClosed;
97 iClients = new Dictionary<string, ClientData>();
98 iStartupManager = new StartupManager();
99 iNotifyIcon = new NotifyIconAdv();
101 //Have our designer initialize its controls
102 InitializeComponent();
104 //Populate device types
105 PopulateDeviceTypes();
107 //Initial status update
110 //We have a bug when drawing minimized and reusing our bitmap
111 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
112 iCreateBitmap = false;
114 //Minimize our window if desired
115 if (Properties.Settings.Default.StartMinimized)
117 WindowState = FormWindowState.Minimized;
125 /// <param name="sender"></param>
126 /// <param name="e"></param>
127 private void MainForm_Load(object sender, EventArgs e)
129 //Check if we are running a Click Once deployed application
130 if (ApplicationDeployment.IsNetworkDeployed)
132 //This is a proper Click Once installation, fetch and show our version number
133 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
137 //Not a proper Click Once installation, assuming development build then
138 this.Text += " - development";
142 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
143 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
144 UpdateAudioDeviceAndMasterVolumeThreadSafe();
147 iNetworkManager = new NetworkManager();
148 iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
149 UpdateNetworkStatus();
151 //Setup notification icon
154 // To make sure start up with minimize to tray works
155 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
161 //When not debugging we want the screen to be empty until a client takes over
164 //When developing we want at least one client for testing
165 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
168 //Open display connection on start-up if needed
169 if (Properties.Settings.Default.DisplayConnectOnStartup)
171 OpenDisplayConnection();
174 //Start our server so that we can get client requests
179 /// Called when our display is opened.
181 /// <param name="aDisplay"></param>
182 private void OnDisplayOpened(Display aDisplay)
184 //Make sure we resume frame rendering
185 iSkipFrameRendering = false;
187 //Set our screen size now that our display is connected
188 //Our panelDisplay is the container of our tableLayoutPanel
189 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
190 //panelDisplay needs an extra 2 pixels for borders on each sides
191 //tableLayoutPanel will eventually be the exact size of our display
192 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
193 panelDisplay.Size = size;
195 //Our display was just opened, update our UI
197 //Initiate asynchronous request
198 iDisplay.RequestFirmwareRevision();
201 UpdateMasterVolumeThreadSafe();
203 UpdateNetworkStatus();
206 //Testing icon in debug, no arm done if icon not supported
207 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
208 //iDisplay.SetAllIconsStatus(2);
214 /// Called when our display is closed.
216 /// <param name="aDisplay"></param>
217 private void OnDisplayClosed(Display aDisplay)
219 //Our display was just closed, update our UI consequently
223 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
225 //Update network status
226 UpdateNetworkStatus();
230 /// Update our Network Status
232 private void UpdateNetworkStatus()
234 if (iDisplay.IsOpen())
236 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconInternet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
237 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
242 int iLastNetworkIconIndex = 0;
243 int iUpdateCountSinceLastNetworkAnimation = 0;
248 private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
250 iUpdateCountSinceLastNetworkAnimation++;
251 iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
253 if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
255 int iconCount=iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal);
258 //Prevents div by zero and other undefined behavior
261 iLastNetworkIconIndex++;
262 iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2);
263 for (int i=0;i<iconCount;i++)
265 if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && !(i == 1 && iLastNetworkIconIndex > 4))
267 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
271 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
280 /// Receive volume change notification and reflect changes on our slider.
282 /// <param name="data"></param>
283 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
285 UpdateMasterVolumeThreadSafe();
289 /// Update master volume when user moves our slider.
291 /// <param name="sender"></param>
292 /// <param name="e"></param>
293 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
295 //Just like Windows Volume Mixer we unmute if the volume is adjusted
296 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
297 //Set volume level according to our volume slider new position
298 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
303 /// Mute check box changed.
305 /// <param name="sender"></param>
306 /// <param name="e"></param>
307 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
309 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
313 /// Device State Changed
315 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
320 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
325 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
328 /// Default Device Changed
330 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
332 if (role == Role.Multimedia && flow == DataFlow.Render)
334 UpdateAudioDeviceAndMasterVolumeThreadSafe();
339 /// Property Value Changed
341 /// <param name="pwstrDeviceId"></param>
342 /// <param name="key"></param>
343 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
349 /// Update master volume indicators based our current system states.
350 /// This typically includes volume levels and mute status.
352 private void UpdateMasterVolumeThreadSafe()
354 if (this.InvokeRequired)
356 //Not in the proper thread, invoke ourselves
357 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
358 this.Invoke(d, new object[] { });
362 //Update volume slider
363 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
364 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
365 //Update mute checkbox
366 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
368 //If our display connection is open we need to update its icons
369 if (iDisplay.IsOpen())
371 //First take care our our volume level icons
372 int volumeIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume);
373 if (volumeIconCount > 0)
375 //Compute current volume level from system level and the number of segments in our display volume bar.
376 //That tells us how many segments in our volume bar needs to be turned on.
377 float currentVolume = volumeLevelScalar * volumeIconCount;
378 int segmentOnCount = Convert.ToInt32(currentVolume);
379 //Check if our segment count was rounded up, this will later be used for half brightness segment
380 bool roundedUp = segmentOnCount > currentVolume;
382 for (int i = 0; i < volumeIconCount; i++)
384 if (i < segmentOnCount)
386 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
387 if (i == segmentOnCount - 1 && roundedUp)
390 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, (iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1)/2);
395 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1);
400 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 0);
405 //Take care our our mute icon
406 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconMute, iMultiMediaDevice.AudioEndpointVolume.Mute);
414 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
416 if (this.InvokeRequired)
418 //Not in the proper thread, invoke ourselves
419 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
420 this.Invoke(d, new object[] { });
424 //We are in the correct thread just go ahead.
427 //Get our master volume
428 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
430 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
432 //Show our volume in our track bar
433 UpdateMasterVolumeThreadSafe();
435 //Register to get volume modifications
436 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
438 trackBarMasterVolume.Enabled = true;
442 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
443 Debug.WriteLine(ex.ToString());
444 //Something went wrong S/PDIF device ca throw exception I guess
445 trackBarMasterVolume.Enabled = false;
452 private void PopulateDeviceTypes()
454 int count = Display.TypeCount();
456 for (int i = 0; i < count; i++)
458 comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
465 private void SetupTrayIcon()
467 iNotifyIcon.Icon = GetIcon("vfd.ico");
468 iNotifyIcon.Text = "Sharp Display Manager";
469 iNotifyIcon.Visible = true;
471 //Double click toggles visibility - typically brings up the application
472 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
477 //Adding a context menu, useful to be able to exit the application
478 ContextMenu contextMenu = new ContextMenu();
479 //Context menu item to toggle visibility
480 MenuItem hideShowItem = new MenuItem("Hide/Show");
481 hideShowItem.Click += delegate(object obj, EventArgs args)
485 contextMenu.MenuItems.Add(hideShowItem);
487 //Context menu item separator
488 contextMenu.MenuItems.Add(new MenuItem("-"));
490 //Context menu exit item
491 MenuItem exitItem = new MenuItem("Exit");
492 exitItem.Click += delegate(object obj, EventArgs args)
496 contextMenu.MenuItems.Add(exitItem);
498 iNotifyIcon.ContextMenu = contextMenu;
502 /// Access icons from embedded resources.
504 /// <param name="name"></param>
505 /// <returns></returns>
506 public static Icon GetIcon(string name)
508 name = "SharpDisplayManager.Resources." + name;
511 Assembly.GetExecutingAssembly().GetManifestResourceNames();
512 for (int i = 0; i < names.Length; i++)
514 if (names[i].Replace('\\', '.') == name)
516 using (Stream stream = Assembly.GetExecutingAssembly().
517 GetManifestResourceStream(names[i]))
519 return new Icon(stream);
529 /// Set our current client.
530 /// This will take care of applying our client layout and set data fields.
532 /// <param name="aSessionId"></param>
533 void SetCurrentClient(string aSessionId)
535 if (aSessionId == iCurrentClientSessionId)
537 //Given client is already the current one.
538 //Don't bother changing anything then.
542 //Set current client ID.
543 iCurrentClientSessionId = aSessionId;
544 //Fetch and set current client data.
545 iCurrentClientData = iClients[aSessionId];
546 //Apply layout and set data fields.
547 UpdateTableLayoutPanel(iCurrentClientData);
550 private void buttonFont_Click(object sender, EventArgs e)
552 //fontDialog.ShowColor = true;
553 //fontDialog.ShowApply = true;
554 fontDialog.ShowEffects = true;
555 fontDialog.Font = cds.Font;
557 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
559 //fontDialog.ShowHelp = true;
561 //fontDlg.MaxSize = 40;
562 //fontDlg.MinSize = 22;
564 //fontDialog.Parent = this;
565 //fontDialog.StartPosition = FormStartPosition.CenterParent;
567 //DlgBox.ShowDialog(fontDialog);
569 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
570 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
572 //Set the fonts to all our labels in our layout
573 foreach (Control ctrl in tableLayoutPanel.Controls)
575 if (ctrl is MarqueeLabel)
577 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
582 cds.Font = fontDialog.Font;
583 Properties.Settings.Default.Save();
592 void CheckFontHeight()
594 //Show font height and width
595 labelFontHeight.Text = "Font height: " + cds.Font.Height;
596 float charWidth = IsFixedWidth(cds.Font);
597 if (charWidth == 0.0f)
599 labelFontWidth.Visible = false;
603 labelFontWidth.Visible = true;
604 labelFontWidth.Text = "Font width: " + charWidth;
607 MarqueeLabel label = null;
608 //Get the first label control we can find
609 foreach (Control ctrl in tableLayoutPanel.Controls)
611 if (ctrl is MarqueeLabel)
613 label = (MarqueeLabel)ctrl;
618 //Now check font height and show a warning if needed.
619 if (label != null && label.Font.Height > label.Height)
621 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
622 labelWarning.Visible = true;
626 labelWarning.Visible = false;
631 private void buttonCapture_Click(object sender, EventArgs e)
633 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
634 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
635 //Bitmap bmpToSave = new Bitmap(bmp);
636 bmp.Save("D:\\capture.png");
638 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
641 string outputFileName = "d:\\capture.png";
642 using (MemoryStream memory = new MemoryStream())
644 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
646 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
647 byte[] bytes = memory.ToArray();
648 fs.Write(bytes, 0, bytes.Length);
655 private void CheckForRequestResults()
657 if (iDisplay.IsRequestPending())
659 switch (iDisplay.AttemptRequestCompletion())
661 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
662 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
663 //Issue next request then
664 iDisplay.RequestPowerSupplyStatus();
667 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
668 if (iDisplay.PowerSupplyStatus())
670 toolStripStatusLabelPower.Text = "ON";
674 toolStripStatusLabelPower.Text = "OFF";
676 //Issue next request then
677 iDisplay.RequestDeviceId();
680 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
681 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
682 //No more request to issue
688 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
690 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
697 public static uint ColorUntouched(int aX, int aY, uint aPixel)
702 public static uint ColorInversed(int aX, int aY, uint aPixel)
707 public static uint ColorChessboard(int aX, int aY, uint aPixel)
709 if ((aX % 2 == 0) && (aY % 2 == 0))
713 else if ((aX % 2 != 0) && (aY % 2 != 0))
721 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
723 return aBmp.Width - aX - 1;
726 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
728 return iBmp.Height - aY - 1;
731 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
736 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
742 /// Select proper pixel delegates according to our current settings.
744 private void SetupPixelDelegates()
746 //Select our pixel processing routine
747 if (cds.InverseColors)
749 //iColorFx = ColorChessboard;
750 iColorFx = ColorInversed;
754 iColorFx = ColorWhiteIsOn;
757 //Select proper coordinate translation functions
758 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
759 if (cds.ReverseScreen)
761 iScreenX = ScreenReversedX;
762 iScreenY = ScreenReversedY;
772 //This is our timer tick responsible to perform our render
773 private void timer_Tick(object sender, EventArgs e)
775 //Update our animations
776 DateTime NewTickTime = DateTime.Now;
778 UpdateNetworkSignal(LastTickTime, NewTickTime);
780 //Update animation for all our marquees
781 foreach (Control ctrl in tableLayoutPanel.Controls)
783 if (ctrl is MarqueeLabel)
785 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
790 if (iDisplay.IsOpen())
792 CheckForRequestResults();
794 //Check if frame rendering is needed
795 //Typically used when showing clock
796 if (!iSkipFrameRendering)
801 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
803 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
804 //iBmp.Save("D:\\capture.png");
806 //Send it to our display
807 for (int i = 0; i < iBmp.Width; i++)
809 for (int j = 0; j < iBmp.Height; j++)
813 //Get our processed pixel coordinates
814 int x = iScreenX(iBmp, i);
815 int y = iScreenY(iBmp, j);
817 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
818 //Apply color effects
819 color = iColorFx(x, y, color);
821 iDisplay.SetPixel(x, y, color);
826 iDisplay.SwapBuffers();
830 //Compute instant FPS
831 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
833 LastTickTime = NewTickTime;
838 /// Attempt to establish connection with our display hardware.
840 private void OpenDisplayConnection()
842 CloseDisplayConnection();
844 if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
847 toolStripStatusLabelConnect.Text = "Connection error";
851 private void CloseDisplayConnection()
853 //Status will be updated upon receiving the closed event
855 if (iDisplay == null || !iDisplay.IsOpen())
860 //Do not clear if we gave up on rendering already.
861 //This means we will keep on displaying clock on MDM166AA for instance.
862 if (!iSkipFrameRendering)
865 iDisplay.SwapBuffers();
868 iDisplay.SetAllIconsStatus(0); //Turn off all icons
872 private void buttonOpen_Click(object sender, EventArgs e)
874 OpenDisplayConnection();
877 private void buttonClose_Click(object sender, EventArgs e)
879 CloseDisplayConnection();
882 private void buttonClear_Click(object sender, EventArgs e)
885 iDisplay.SwapBuffers();
888 private void buttonFill_Click(object sender, EventArgs e)
891 iDisplay.SwapBuffers();
894 private void trackBarBrightness_Scroll(object sender, EventArgs e)
896 cds.Brightness = trackBarBrightness.Value;
897 Properties.Settings.Default.Save();
898 iDisplay.SetBrightness(trackBarBrightness.Value);
904 /// CDS stands for Current Display Settings
906 private DisplaySettings cds
910 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
911 if (settings == null)
913 settings = new DisplaysSettings();
915 Properties.Settings.Default.DisplaysSettings = settings;
918 //Make sure all our settings have been created
919 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
921 settings.Displays.Add(new DisplaySettings());
924 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
925 return displaySettings;
930 /// Check if the given font has a fixed character pitch.
932 /// <param name="ft"></param>
933 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
934 public float IsFixedWidth(Font ft)
936 Graphics g = CreateGraphics();
937 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
938 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
940 bool fixedWidth = true;
942 foreach (char c in charSizes)
943 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
955 /// Synchronize UI with settings
957 private void UpdateStatus()
960 checkBoxShowBorders.Checked = cds.ShowBorders;
961 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
963 //Set the proper font to each of our labels
964 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
966 ctrl.Font = cds.Font;
970 //Check if "run on Windows startup" is enabled
971 checkBoxAutoStart.Checked = iStartupManager.Startup;
973 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
974 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
975 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
976 checkBoxReverseScreen.Checked = cds.ReverseScreen;
977 checkBoxInverseColors.Checked = cds.InverseColors;
978 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
979 checkBoxScaleToFit.Checked = cds.ScaleToFit;
980 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
981 labelMinFontSize.Enabled = cds.ScaleToFit;
982 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
983 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
984 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
985 timer.Interval = cds.TimerInterval;
986 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
987 textBoxScrollLoopSeparator.Text = cds.Separator;
989 SetupPixelDelegates();
991 if (iDisplay.IsOpen())
993 //We have a display connection
994 //Reflect that in our UI
996 tableLayoutPanel.Enabled = true;
997 panelDisplay.Enabled = true;
999 //Only setup brightness if display is open
1000 trackBarBrightness.Minimum = iDisplay.MinBrightness();
1001 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
1002 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
1004 //Brightness out of range, this can occur when using auto-detect
1005 //Use max brightness instead
1006 trackBarBrightness.Value = iDisplay.MaxBrightness();
1007 iDisplay.SetBrightness(iDisplay.MaxBrightness());
1011 trackBarBrightness.Value = cds.Brightness;
1012 iDisplay.SetBrightness(cds.Brightness);
1015 //Try compute the steps to something that makes sense
1016 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
1017 trackBarBrightness.SmallChange = 1;
1020 buttonFill.Enabled = true;
1021 buttonClear.Enabled = true;
1022 buttonOpen.Enabled = false;
1023 buttonClose.Enabled = true;
1024 trackBarBrightness.Enabled = true;
1025 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1026 //+ " - " + iDisplay.SerialNumber();
1028 if (iDisplay.SupportPowerOnOff())
1030 buttonPowerOn.Enabled = true;
1031 buttonPowerOff.Enabled = true;
1035 buttonPowerOn.Enabled = false;
1036 buttonPowerOff.Enabled = false;
1039 if (iDisplay.SupportClock())
1041 buttonShowClock.Enabled = true;
1042 buttonHideClock.Enabled = true;
1046 buttonShowClock.Enabled = false;
1047 buttonHideClock.Enabled = false;
1051 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1052 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel)>0;
1054 if (cds.ShowVolumeLabel)
1056 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1060 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1065 //Display is connection not available
1066 //Reflect that in our UI
1067 checkBoxShowVolumeLabel.Enabled = false;
1068 tableLayoutPanel.Enabled = false;
1069 panelDisplay.Enabled = false;
1070 buttonFill.Enabled = false;
1071 buttonClear.Enabled = false;
1072 buttonOpen.Enabled = true;
1073 buttonClose.Enabled = false;
1074 trackBarBrightness.Enabled = false;
1075 buttonPowerOn.Enabled = false;
1076 buttonPowerOff.Enabled = false;
1077 buttonShowClock.Enabled = false;
1078 buttonHideClock.Enabled = false;
1079 toolStripStatusLabelConnect.Text = "Disconnected";
1080 toolStripStatusLabelPower.Text = "N/A";
1089 /// <param name="sender"></param>
1090 /// <param name="e"></param>
1091 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1093 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1094 Properties.Settings.Default.Save();
1098 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1100 //Save our show borders setting
1101 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1102 cds.ShowBorders = checkBoxShowBorders.Checked;
1103 Properties.Settings.Default.Save();
1107 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1109 //Save our connect on startup setting
1110 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1111 Properties.Settings.Default.Save();
1114 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1116 //Save our "Minimize to tray" setting
1117 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1118 Properties.Settings.Default.Save();
1122 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1124 //Save our "Start minimized" setting
1125 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1126 Properties.Settings.Default.Save();
1129 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1131 iStartupManager.Startup = checkBoxAutoStart.Checked;
1135 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1137 //Save our reverse screen setting
1138 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1139 Properties.Settings.Default.Save();
1140 SetupPixelDelegates();
1143 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1145 //Save our inverse colors setting
1146 cds.InverseColors = checkBoxInverseColors.Checked;
1147 Properties.Settings.Default.Save();
1148 SetupPixelDelegates();
1151 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1153 //Save our scale to fit setting
1154 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1155 Properties.Settings.Default.Save();
1157 labelMinFontSize.Enabled = cds.ScaleToFit;
1158 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1161 private void MainForm_Resize(object sender, EventArgs e)
1163 if (WindowState == FormWindowState.Minimized)
1166 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1167 iCreateBitmap = true;
1171 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1173 iNetworkManager.Dispose();
1174 CloseDisplayConnection();
1176 e.Cancel = iClosing;
1179 public void StartServer()
1181 iServiceHost = new ServiceHost
1184 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1187 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1188 iServiceHost.Open();
1191 public void StopServer()
1193 if (iClients.Count > 0 && !iClosing)
1197 BroadcastCloseEvent();
1201 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1203 iClosing = false; //We make sure we force close if asked twice
1208 //We removed that as it often lags for some reason
1209 //iServiceHost.Close();
1213 public void BroadcastCloseEvent()
1215 Trace.TraceInformation("BroadcastCloseEvent - start");
1217 var inactiveClients = new List<string>();
1218 foreach (var client in iClients)
1220 //if (client.Key != eventData.ClientName)
1224 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1225 client.Value.Callback.OnCloseOrder(/*eventData*/);
1227 catch (Exception ex)
1229 inactiveClients.Add(client.Key);
1234 if (inactiveClients.Count > 0)
1236 foreach (var client in inactiveClients)
1238 iClients.Remove(client);
1239 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1243 if (iClients.Count==0)
1250 /// Just remove all our fields.
1252 private void ClearLayout()
1254 tableLayoutPanel.Controls.Clear();
1255 tableLayoutPanel.RowStyles.Clear();
1256 tableLayoutPanel.ColumnStyles.Clear();
1257 iCurrentClientData = null;
1261 /// Just launch a demo client.
1263 private void StartNewClient(string aTopText = "", string aBottomText = "")
1265 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1266 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1267 clientThread.Start(myParams);
1271 private void buttonStartClient_Click(object sender, EventArgs e)
1276 private void buttonSuspend_Click(object sender, EventArgs e)
1278 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1279 timer.Enabled = !timer.Enabled;
1282 buttonSuspend.Text = "Run";
1286 buttonSuspend.Text = "Pause";
1290 private void buttonCloseClients_Click(object sender, EventArgs e)
1292 BroadcastCloseEvent();
1295 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1304 /// <param name="aSessionId"></param>
1305 /// <param name="aCallback"></param>
1306 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1308 if (this.InvokeRequired)
1310 //Not in the proper thread, invoke ourselves
1311 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1312 this.Invoke(d, new object[] { aSessionId, aCallback });
1316 //We are in the proper thread
1317 //Add this session to our collection of clients
1318 ClientData newClient = new ClientData(aSessionId, aCallback);
1319 Program.iMainForm.iClients.Add(aSessionId, newClient);
1320 //Add this session to our UI
1321 UpdateClientTreeViewNode(newClient);
1328 /// <param name="aSessionId"></param>
1329 public void RemoveClientThreadSafe(string aSessionId)
1331 if (this.InvokeRequired)
1333 //Not in the proper thread, invoke ourselves
1334 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1335 this.Invoke(d, new object[] { aSessionId });
1339 //We are in the proper thread
1340 //Remove this session from both client collection and UI tree view
1341 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1343 Program.iMainForm.iClients.Remove(aSessionId);
1344 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1347 if (iClients.Count == 0)
1349 //Clear our screen when last client disconnects
1354 //We were closing our form
1355 //All clients are now closed
1356 //Just resume our close operation
1367 /// <param name="aSessionId"></param>
1368 /// <param name="aLayout"></param>
1369 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1371 if (this.InvokeRequired)
1373 //Not in the proper thread, invoke ourselves
1374 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1375 this.Invoke(d, new object[] { aSessionId, aLayout });
1379 ClientData client = iClients[aSessionId];
1382 client.Layout = aLayout;
1383 UpdateTableLayoutPanel(client);
1385 UpdateClientTreeViewNode(client);
1393 /// <param name="aSessionId"></param>
1394 /// <param name="aField"></param>
1395 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1397 if (this.InvokeRequired)
1399 //Not in the proper thread, invoke ourselves
1400 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1401 this.Invoke(d, new object[] { aSessionId, aField });
1405 //We are in the proper thread
1406 //Call the non-thread-safe variant
1407 SetClientField(aSessionId, aField);
1414 /// <param name="aSessionId"></param>
1415 /// <param name="aField"></param>
1416 private void SetClientField(string aSessionId, DataField aField)
1418 SetCurrentClient(aSessionId);
1419 ClientData client = iClients[aSessionId];
1422 bool somethingChanged = false;
1424 //Make sure all our fields are in place
1425 while (client.Fields.Count < (aField.Index + 1))
1427 //Add a text field with proper index
1428 client.Fields.Add(new DataField(client.Fields.Count));
1429 somethingChanged = true;
1432 if (client.Fields[aField.Index].IsSameLayout(aField))
1434 //Same layout just update our field
1435 client.Fields[aField.Index] = aField;
1437 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1439 //Text field control already in place, just change the text
1440 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1441 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1442 label.Text = aField.Text;
1443 label.TextAlign = aField.Alignment;
1445 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1447 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1448 //Bitmap field control already in place just change the bitmap
1449 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1450 pictureBox.Image = aField.Bitmap;
1454 somethingChanged = true;
1455 //The requested control in our layout it not of the correct type
1456 //Wrong control type, re-create them all
1457 UpdateTableLayoutPanel(iCurrentClientData);
1462 somethingChanged = true;
1463 //Different layout, need to rebuild it
1464 client.Fields[aField.Index] = aField;
1465 UpdateTableLayoutPanel(iCurrentClientData);
1469 if (somethingChanged)
1471 UpdateClientTreeViewNode(client);
1479 /// <param name="aTexts"></param>
1480 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1482 if (this.InvokeRequired)
1484 //Not in the proper thread, invoke ourselves
1485 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1486 this.Invoke(d, new object[] { aSessionId, aFields });
1490 //Put each our text fields in a label control
1491 foreach (DataField field in aFields)
1493 SetClientField(aSessionId, field);
1501 /// <param name="aSessionId"></param>
1502 /// <param name="aName"></param>
1503 public void SetClientNameThreadSafe(string aSessionId, string aName)
1505 if (this.InvokeRequired)
1507 //Not in the proper thread, invoke ourselves
1508 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1509 this.Invoke(d, new object[] { aSessionId, aName });
1513 //We are in the proper thread
1515 ClientData client = iClients[aSessionId];
1519 client.Name = aName;
1520 //Update our tree-view
1521 UpdateClientTreeViewNode(client);
1529 /// <param name="aClient"></param>
1530 private void UpdateClientTreeViewNode(ClientData aClient)
1532 if (aClient == null)
1537 TreeNode node = null;
1538 //Check that our client node already exists
1539 //Get our client root node using its key which is our session ID
1540 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1541 if (nodes.Count()>0)
1543 //We already have a node for that client
1545 //Clear children as we are going to recreate them below
1550 //Client node does not exists create a new one
1551 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1552 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1558 if (aClient.Name != "")
1560 //We have a name, us it as text for our root node
1561 node.Text = aClient.Name;
1562 //Add a child with SessionId
1563 node.Nodes.Add(new TreeNode(aClient.SessionId));
1567 //No name, use session ID instead
1568 node.Text = aClient.SessionId;
1571 if (aClient.Fields.Count > 0)
1573 //Create root node for our texts
1574 TreeNode textsRoot = new TreeNode("Fields");
1575 node.Nodes.Add(textsRoot);
1576 //For each text add a new entry
1577 foreach (DataField field in aClient.Fields)
1579 if (!field.IsBitmap)
1581 DataField textField = (DataField)field;
1582 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1586 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1596 /// Update our table layout row styles to make sure each rows have similar height
1598 private void UpdateTableLayoutRowStyles()
1600 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1602 rowStyle.SizeType = SizeType.Percent;
1603 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1608 /// Update our display table layout.
1610 /// <param name="aLayout"></param>
1611 private void UpdateTableLayoutPanel(ClientData aClient)
1613 if (aClient == null)
1620 TableLayout layout = aClient.Layout;
1623 tableLayoutPanel.Controls.Clear();
1624 tableLayoutPanel.RowStyles.Clear();
1625 tableLayoutPanel.ColumnStyles.Clear();
1626 tableLayoutPanel.RowCount = 0;
1627 tableLayoutPanel.ColumnCount = 0;
1629 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1631 tableLayoutPanel.RowCount++;
1634 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1636 tableLayoutPanel.ColumnCount++;
1639 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1641 //Create our column styles
1642 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1644 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1648 //Create our row styles
1649 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1652 //Check if we already have a control
1653 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1654 if (existingControl!=null)
1656 //We already have a control in that cell as a results of row/col spanning
1657 //Move on to next cell then
1663 //Check if a client field already exists for that cell
1664 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1666 //No client field specified, create a text field by default
1667 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1670 //Create a control corresponding to the field specified for that cell
1671 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1672 Control control = CreateControlForDataField(field);
1674 //Add newly created control to our table layout at the specified row and column
1675 tableLayoutPanel.Controls.Add(control, i, j);
1676 //Make sure we specify row and column span for that new control
1677 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1678 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1683 while (aClient.Fields.Count > fieldCount)
1685 //We have too much fields for this layout
1686 //Just discard them until we get there
1687 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1694 /// Check our type of data field and create corresponding control
1696 /// <param name="aField"></param>
1697 private Control CreateControlForDataField(DataField aField)
1699 Control control=null;
1700 if (!aField.IsBitmap)
1702 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1703 label.AutoEllipsis = true;
1704 label.AutoSize = true;
1705 label.BackColor = System.Drawing.Color.Transparent;
1706 label.Dock = System.Windows.Forms.DockStyle.Fill;
1707 label.Location = new System.Drawing.Point(1, 1);
1708 label.Margin = new System.Windows.Forms.Padding(0);
1709 label.Name = "marqueeLabel" + aField.Index;
1710 label.OwnTimer = false;
1711 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1712 label.Separator = cds.Separator;
1713 label.MinFontSize = cds.MinFontSize;
1714 label.ScaleToFit = cds.ScaleToFit;
1715 //control.Size = new System.Drawing.Size(254, 30);
1716 //control.TabIndex = 2;
1717 label.Font = cds.Font;
1719 label.TextAlign = aField.Alignment;
1720 label.UseCompatibleTextRendering = true;
1721 label.Text = aField.Text;
1727 //Create picture box
1728 PictureBox picture = new PictureBox();
1729 picture.AutoSize = true;
1730 picture.BackColor = System.Drawing.Color.Transparent;
1731 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1732 picture.Location = new System.Drawing.Point(1, 1);
1733 picture.Margin = new System.Windows.Forms.Padding(0);
1734 picture.Name = "pictureBox" + aField;
1736 picture.Image = aField.Bitmap;
1745 /// Called when the user selected a new display type.
1747 /// <param name="sender"></param>
1748 /// <param name="e"></param>
1749 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1751 //Store the selected display type in our settings
1752 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1753 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1754 Properties.Settings.Default.Save();
1756 //Try re-opening the display connection if we were already connected.
1757 //Otherwise just update our status to reflect display type change.
1758 if (iDisplay.IsOpen())
1760 OpenDisplayConnection();
1768 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1770 if (maskedTextBoxTimerInterval.Text != "")
1772 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1776 timer.Interval = interval;
1777 cds.TimerInterval = timer.Interval;
1778 Properties.Settings.Default.Save();
1783 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1785 if (maskedTextBoxMinFontSize.Text != "")
1787 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1789 if (minFontSize > 0)
1791 cds.MinFontSize = minFontSize;
1792 Properties.Settings.Default.Save();
1793 //We need to recreate our layout for that change to take effect
1794 UpdateTableLayoutPanel(iCurrentClientData);
1800 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1802 if (maskedTextBoxScrollingSpeed.Text != "")
1804 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1806 if (scrollingSpeed > 0)
1808 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1809 Properties.Settings.Default.Save();
1810 //We need to recreate our layout for that change to take effect
1811 UpdateTableLayoutPanel(iCurrentClientData);
1816 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1818 cds.Separator = textBoxScrollLoopSeparator.Text;
1819 Properties.Settings.Default.Save();
1821 //Update our text fields
1822 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1824 ctrl.Separator = cds.Separator;
1829 private void buttonPowerOn_Click(object sender, EventArgs e)
1834 private void buttonPowerOff_Click(object sender, EventArgs e)
1836 iDisplay.PowerOff();
1839 private void buttonShowClock_Click(object sender, EventArgs e)
1844 private void buttonHideClock_Click(object sender, EventArgs e)
1849 private void buttonUpdate_Click(object sender, EventArgs e)
1851 InstallUpdateSyncWithInfo();
1859 if (!iDisplay.IsOpen())
1864 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1865 iSkipFrameRendering = true;
1868 iDisplay.SwapBuffers();
1869 //Then show our clock
1870 iDisplay.ShowClock();
1878 if (!iDisplay.IsOpen())
1883 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1884 iSkipFrameRendering = false;
1885 iDisplay.HideClock();
1888 private void InstallUpdateSyncWithInfo()
1890 UpdateCheckInfo info = null;
1892 if (ApplicationDeployment.IsNetworkDeployed)
1894 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1898 info = ad.CheckForDetailedUpdate();
1901 catch (DeploymentDownloadException dde)
1903 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);
1906 catch (InvalidDeploymentException ide)
1908 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);
1911 catch (InvalidOperationException ioe)
1913 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1917 if (info.UpdateAvailable)
1919 Boolean doUpdate = true;
1921 if (!info.IsUpdateRequired)
1923 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1924 if (!(DialogResult.OK == dr))
1931 // Display a message that the app MUST reboot. Display the minimum required version.
1932 MessageBox.Show("This application has detected a mandatory update from your current " +
1933 "version to version " + info.MinimumRequiredVersion.ToString() +
1934 ". The application will now install the update and restart.",
1935 "Update Available", MessageBoxButtons.OK,
1936 MessageBoxIcon.Information);
1944 MessageBox.Show("The application has been upgraded, and will now restart.");
1945 Application.Restart();
1947 catch (DeploymentDownloadException dde)
1949 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1956 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1965 private void SysTrayHideShow()
1971 WindowState = FormWindowState.Normal;
1976 /// Use to handle minimize events.
1978 /// <param name="sender"></param>
1979 /// <param name="e"></param>
1980 private void MainForm_SizeChanged(object sender, EventArgs e)
1982 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
1995 /// <param name="sender"></param>
1996 /// <param name="e"></param>
1997 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
1999 //Our table layout size has changed which means our display size has changed.
2000 //We need to re-create our bitmap.
2001 iCreateBitmap = true;
2006 /// A UI thread copy of a client relevant data.
2007 /// Keeping this copy in the UI thread helps us deal with threading issues.
2009 public class ClientData
2011 public ClientData(string aSessionId, ICallback aCallback)
2013 SessionId = aSessionId;
2015 Fields = new List<DataField>();
2016 Layout = new TableLayout(1, 2); //Default to one column and two rows
2017 Callback = aCallback;
2020 public string SessionId { get; set; }
2021 public string Name { get; set; }
2022 public List<DataField> Fields { get; set; }
2023 public TableLayout Layout { get; set; }
2024 public ICallback Callback { get; set; }