Documentation and minor clean-up.
2 // Copyright (C) 2014-2015 Stéphane Lenclud.
4 // This file is part of SharpDisplayManager.
6 // SharpDisplayManager is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // SharpDisplayManager is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with SharpDisplayManager. If not, see <http://www.gnu.org/licenses/>.
21 using System.Collections.Generic;
22 using System.ComponentModel;
27 using System.Threading.Tasks;
28 using System.Windows.Forms;
30 using CodeProject.Dialog;
31 using System.Drawing.Imaging;
32 using System.ServiceModel;
33 using System.Threading;
34 using System.Diagnostics;
35 using System.Deployment.Application;
36 using System.Reflection;
38 using NAudio.CoreAudioApi;
39 using NAudio.CoreAudioApi.Interfaces;
40 using System.Runtime.InteropServices;
44 using SharpDisplayClient;
46 using MiniDisplayInterop;
49 namespace SharpDisplayManager
52 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
53 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
54 //Delegates are used for our thread safe method
55 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
56 public delegate void RemoveClientDelegate(string aSessionId);
57 public delegate void SetFieldDelegate(string SessionId, DataField aField);
58 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
59 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
60 public delegate void SetClientNameDelegate(string aSessionId, string aName);
61 public delegate void PlainUpdateDelegate();
65 /// Our Display manager main form
67 [System.ComponentModel.DesignerCategory("Form")]
68 public partial class MainForm : MainFormHid, IMMNotificationClient
70 DateTime LastTickTime;
72 System.Drawing.Bitmap iBmp;
73 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
74 ServiceHost iServiceHost;
75 // Our collection of clients sorted by session id.
76 public Dictionary<string, ClientData> iClients;
77 // The name of the client which informations are currently displayed.
78 public string iCurrentClientSessionId;
79 ClientData iCurrentClientData;
83 public bool iSkipFrameRendering;
84 //Function pointer for pixel color filtering
85 ColorProcessingDelegate iColorFx;
86 //Function pointer for pixel X coordinate intercept
87 CoordinateTranslationDelegate iScreenX;
88 //Function pointer for pixel Y coordinate intercept
89 CoordinateTranslationDelegate iScreenY;
91 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
92 private MMDevice iMultiMediaDevice;
94 private NetworkManager iNetworkManager;
98 /// Manage run when Windows startup option
100 private StartupManager iStartupManager;
103 /// System tray icon.
105 private NotifyIconAdv iNotifyIcon;
109 iSkipFrameRendering = false;
111 iCurrentClientSessionId = "";
112 iCurrentClientData = null;
113 LastTickTime = DateTime.Now;
114 //Instantiate our display and register for events notifications
115 iDisplay = new Display();
116 iDisplay.OnOpened += OnDisplayOpened;
117 iDisplay.OnClosed += OnDisplayClosed;
119 iClients = new Dictionary<string, ClientData>();
120 iStartupManager = new StartupManager();
121 iNotifyIcon = new NotifyIconAdv();
123 //Have our designer initialize its controls
124 InitializeComponent();
126 //Populate device types
127 PopulateDeviceTypes();
129 //Initial status update
132 //We have a bug when drawing minimized and reusing our bitmap
133 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
134 iCreateBitmap = false;
136 //Minimize our window if desired
137 if (Properties.Settings.Default.StartMinimized)
139 WindowState = FormWindowState.Minimized;
147 /// <param name="sender"></param>
148 /// <param name="e"></param>
149 private void MainForm_Load(object sender, EventArgs e)
151 //Check if we are running a Click Once deployed application
152 if (ApplicationDeployment.IsNetworkDeployed)
154 //This is a proper Click Once installation, fetch and show our version number
155 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
159 //Not a proper Click Once installation, assuming development build then
160 this.Text += " - development";
164 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
165 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
166 UpdateAudioDeviceAndMasterVolumeThreadSafe();
169 iNetworkManager = new NetworkManager();
170 iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
171 UpdateNetworkStatus();
173 //Setup notification icon
176 // To make sure start up with minimize to tray works
177 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
183 //When not debugging we want the screen to be empty until a client takes over
186 //When developing we want at least one client for testing
187 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
190 //Open display connection on start-up if needed
191 if (Properties.Settings.Default.DisplayConnectOnStartup)
193 OpenDisplayConnection();
196 //Start our server so that we can get client requests
199 //Register for HID events
200 RegisterHidDevices();
204 /// Called when our display is opened.
206 /// <param name="aDisplay"></param>
207 private void OnDisplayOpened(Display aDisplay)
209 //Make sure we resume frame rendering
210 iSkipFrameRendering = false;
212 //Set our screen size now that our display is connected
213 //Our panelDisplay is the container of our tableLayoutPanel
214 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
215 //panelDisplay needs an extra 2 pixels for borders on each sides
216 //tableLayoutPanel will eventually be the exact size of our display
217 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
218 panelDisplay.Size = size;
220 //Our display was just opened, update our UI
222 //Initiate asynchronous request
223 iDisplay.RequestFirmwareRevision();
226 UpdateMasterVolumeThreadSafe();
228 UpdateNetworkStatus();
231 //Testing icon in debug, no arm done if icon not supported
232 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
233 //iDisplay.SetAllIconsStatus(2);
239 /// Called when our display is closed.
241 /// <param name="aDisplay"></param>
242 private void OnDisplayClosed(Display aDisplay)
244 //Our display was just closed, update our UI consequently
248 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
250 //Update network status
251 UpdateNetworkStatus();
255 /// Update our Network Status
257 private void UpdateNetworkStatus()
259 if (iDisplay.IsOpen())
261 iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
262 iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
267 int iLastNetworkIconIndex = 0;
268 int iUpdateCountSinceLastNetworkAnimation = 0;
273 private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
275 iUpdateCountSinceLastNetworkAnimation++;
276 iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
278 if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
280 int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal);
283 //Prevents div by zero and other undefined behavior
286 iLastNetworkIconIndex++;
287 iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2);
288 for (int i=0;i<iconCount;i++)
290 if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && !(i == 1 && iLastNetworkIconIndex > 4))
292 iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i);
296 iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i);
305 /// Receive volume change notification and reflect changes on our slider.
307 /// <param name="data"></param>
308 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
310 UpdateMasterVolumeThreadSafe();
314 /// Update master volume when user moves our slider.
316 /// <param name="sender"></param>
317 /// <param name="e"></param>
318 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
320 //Just like Windows Volume Mixer we unmute if the volume is adjusted
321 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
322 //Set volume level according to our volume slider new position
323 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
328 /// Mute check box changed.
330 /// <param name="sender"></param>
331 /// <param name="e"></param>
332 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
334 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
338 /// Device State Changed
340 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
345 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
350 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
353 /// Default Device Changed
355 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
357 if (role == Role.Multimedia && flow == DataFlow.Render)
359 UpdateAudioDeviceAndMasterVolumeThreadSafe();
364 /// Property Value Changed
366 /// <param name="pwstrDeviceId"></param>
367 /// <param name="key"></param>
368 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
374 /// Update master volume indicators based our current system states.
375 /// This typically includes volume levels and mute status.
377 private void UpdateMasterVolumeThreadSafe()
379 if (this.InvokeRequired)
381 //Not in the proper thread, invoke ourselves
382 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
383 this.Invoke(d, new object[] { });
387 //Update volume slider
388 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
389 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
390 //Update mute checkbox
391 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
393 //If our display connection is open we need to update its icons
394 if (iDisplay.IsOpen())
396 //First take care our our volume level icons
397 int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume);
398 if (volumeIconCount > 0)
400 //Compute current volume level from system level and the number of segments in our display volume bar.
401 //That tells us how many segments in our volume bar needs to be turned on.
402 float currentVolume = volumeLevelScalar * volumeIconCount;
403 int segmentOnCount = Convert.ToInt32(currentVolume);
404 //Check if our segment count was rounded up, this will later be used for half brightness segment
405 bool roundedUp = segmentOnCount > currentVolume;
407 for (int i = 0; i < volumeIconCount; i++)
409 if (i < segmentOnCount)
411 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
412 if (i == segmentOnCount - 1 && roundedUp)
415 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1) / 2);
420 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1);
425 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0);
430 //Take care our our mute icon
431 iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iMultiMediaDevice.AudioEndpointVolume.Mute);
439 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
441 if (this.InvokeRequired)
443 //Not in the proper thread, invoke ourselves
444 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
445 this.Invoke(d, new object[] { });
449 //We are in the correct thread just go ahead.
452 //Get our master volume
453 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
455 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
457 //Show our volume in our track bar
458 UpdateMasterVolumeThreadSafe();
460 //Register to get volume modifications
461 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
463 trackBarMasterVolume.Enabled = true;
467 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
468 Debug.WriteLine(ex.ToString());
469 //Something went wrong S/PDIF device ca throw exception I guess
470 trackBarMasterVolume.Enabled = false;
477 private void PopulateDeviceTypes()
479 int count = Display.TypeCount();
481 for (int i = 0; i < count; i++)
483 comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type)i));
490 private void SetupTrayIcon()
492 iNotifyIcon.Icon = GetIcon("vfd.ico");
493 iNotifyIcon.Text = "Sharp Display Manager";
494 iNotifyIcon.Visible = true;
496 //Double click toggles visibility - typically brings up the application
497 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
502 //Adding a context menu, useful to be able to exit the application
503 ContextMenu contextMenu = new ContextMenu();
504 //Context menu item to toggle visibility
505 MenuItem hideShowItem = new MenuItem("Hide/Show");
506 hideShowItem.Click += delegate(object obj, EventArgs args)
510 contextMenu.MenuItems.Add(hideShowItem);
512 //Context menu item separator
513 contextMenu.MenuItems.Add(new MenuItem("-"));
515 //Context menu exit item
516 MenuItem exitItem = new MenuItem("Exit");
517 exitItem.Click += delegate(object obj, EventArgs args)
521 contextMenu.MenuItems.Add(exitItem);
523 iNotifyIcon.ContextMenu = contextMenu;
527 /// Access icons from embedded resources.
529 /// <param name="name"></param>
530 /// <returns></returns>
531 public static Icon GetIcon(string name)
533 name = "SharpDisplayManager.Resources." + name;
536 Assembly.GetExecutingAssembly().GetManifestResourceNames();
537 for (int i = 0; i < names.Length; i++)
539 if (names[i].Replace('\\', '.') == name)
541 using (Stream stream = Assembly.GetExecutingAssembly().
542 GetManifestResourceStream(names[i]))
544 return new Icon(stream);
554 /// Set our current client.
555 /// This will take care of applying our client layout and set data fields.
557 /// <param name="aSessionId"></param>
558 void SetCurrentClient(string aSessionId)
560 if (aSessionId == iCurrentClientSessionId)
562 //Given client is already the current one.
563 //Don't bother changing anything then.
567 //Set current client ID.
568 iCurrentClientSessionId = aSessionId;
569 //Fetch and set current client data.
570 iCurrentClientData = iClients[aSessionId];
571 //Apply layout and set data fields.
572 UpdateTableLayoutPanel(iCurrentClientData);
575 private void buttonFont_Click(object sender, EventArgs e)
577 //fontDialog.ShowColor = true;
578 //fontDialog.ShowApply = true;
579 fontDialog.ShowEffects = true;
580 fontDialog.Font = cds.Font;
582 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
584 //fontDialog.ShowHelp = true;
586 //fontDlg.MaxSize = 40;
587 //fontDlg.MinSize = 22;
589 //fontDialog.Parent = this;
590 //fontDialog.StartPosition = FormStartPosition.CenterParent;
592 //DlgBox.ShowDialog(fontDialog);
594 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
595 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
597 //Set the fonts to all our labels in our layout
598 foreach (Control ctrl in tableLayoutPanel.Controls)
600 if (ctrl is MarqueeLabel)
602 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
607 cds.Font = fontDialog.Font;
608 Properties.Settings.Default.Save();
617 void CheckFontHeight()
619 //Show font height and width
620 labelFontHeight.Text = "Font height: " + cds.Font.Height;
621 float charWidth = IsFixedWidth(cds.Font);
622 if (charWidth == 0.0f)
624 labelFontWidth.Visible = false;
628 labelFontWidth.Visible = true;
629 labelFontWidth.Text = "Font width: " + charWidth;
632 MarqueeLabel label = null;
633 //Get the first label control we can find
634 foreach (Control ctrl in tableLayoutPanel.Controls)
636 if (ctrl is MarqueeLabel)
638 label = (MarqueeLabel)ctrl;
643 //Now check font height and show a warning if needed.
644 if (label != null && label.Font.Height > label.Height)
646 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
647 labelWarning.Visible = true;
651 labelWarning.Visible = false;
656 private void buttonCapture_Click(object sender, EventArgs e)
658 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
659 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
660 //Bitmap bmpToSave = new Bitmap(bmp);
661 bmp.Save("D:\\capture.png");
663 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
666 string outputFileName = "d:\\capture.png";
667 using (MemoryStream memory = new MemoryStream())
669 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
671 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
672 byte[] bytes = memory.ToArray();
673 fs.Write(bytes, 0, bytes.Length);
680 private void CheckForRequestResults()
682 if (iDisplay.IsRequestPending())
684 switch (iDisplay.AttemptRequestCompletion())
686 case MiniDisplay.Request.FirmwareRevision:
687 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
688 //Issue next request then
689 iDisplay.RequestPowerSupplyStatus();
692 case MiniDisplay.Request.PowerSupplyStatus:
693 if (iDisplay.PowerSupplyStatus())
695 toolStripStatusLabelPower.Text = "ON";
699 toolStripStatusLabelPower.Text = "OFF";
701 //Issue next request then
702 iDisplay.RequestDeviceId();
705 case MiniDisplay.Request.DeviceId:
706 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
707 //No more request to issue
713 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
715 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
722 public static uint ColorUntouched(int aX, int aY, uint aPixel)
727 public static uint ColorInversed(int aX, int aY, uint aPixel)
732 public static uint ColorChessboard(int aX, int aY, uint aPixel)
734 if ((aX % 2 == 0) && (aY % 2 == 0))
738 else if ((aX % 2 != 0) && (aY % 2 != 0))
746 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
748 return aBmp.Width - aX - 1;
751 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
753 return iBmp.Height - aY - 1;
756 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
761 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
767 /// Select proper pixel delegates according to our current settings.
769 private void SetupPixelDelegates()
771 //Select our pixel processing routine
772 if (cds.InverseColors)
774 //iColorFx = ColorChessboard;
775 iColorFx = ColorInversed;
779 iColorFx = ColorWhiteIsOn;
782 //Select proper coordinate translation functions
783 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
784 if (cds.ReverseScreen)
786 iScreenX = ScreenReversedX;
787 iScreenY = ScreenReversedY;
797 //This is our timer tick responsible to perform our render
798 private void timer_Tick(object sender, EventArgs e)
800 //Update our animations
801 DateTime NewTickTime = DateTime.Now;
803 UpdateNetworkSignal(LastTickTime, NewTickTime);
805 //Update animation for all our marquees
806 foreach (Control ctrl in tableLayoutPanel.Controls)
808 if (ctrl is MarqueeLabel)
810 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
815 if (iDisplay.IsOpen())
817 CheckForRequestResults();
819 //Check if frame rendering is needed
820 //Typically used when showing clock
821 if (!iSkipFrameRendering)
826 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
828 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
829 //iBmp.Save("D:\\capture.png");
831 //Send it to our display
832 for (int i = 0; i < iBmp.Width; i++)
834 for (int j = 0; j < iBmp.Height; j++)
838 //Get our processed pixel coordinates
839 int x = iScreenX(iBmp, i);
840 int y = iScreenY(iBmp, j);
842 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
843 //Apply color effects
844 color = iColorFx(x, y, color);
846 iDisplay.SetPixel(x, y, color);
851 iDisplay.SwapBuffers();
855 //Compute instant FPS
856 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
858 LastTickTime = NewTickTime;
863 /// Attempt to establish connection with our display hardware.
865 private void OpenDisplayConnection()
867 CloseDisplayConnection();
869 if (!iDisplay.Open((MiniDisplay.Type)cds.DisplayType))
872 toolStripStatusLabelConnect.Text = "Connection error";
876 private void CloseDisplayConnection()
878 //Status will be updated upon receiving the closed event
880 if (iDisplay == null || !iDisplay.IsOpen())
885 //Do not clear if we gave up on rendering already.
886 //This means we will keep on displaying clock on MDM166AA for instance.
887 if (!iSkipFrameRendering)
890 iDisplay.SwapBuffers();
893 iDisplay.SetAllIconsStatus(0); //Turn off all icons
897 private void buttonOpen_Click(object sender, EventArgs e)
899 OpenDisplayConnection();
902 private void buttonClose_Click(object sender, EventArgs e)
904 CloseDisplayConnection();
907 private void buttonClear_Click(object sender, EventArgs e)
910 iDisplay.SwapBuffers();
913 private void buttonFill_Click(object sender, EventArgs e)
916 iDisplay.SwapBuffers();
919 private void trackBarBrightness_Scroll(object sender, EventArgs e)
921 cds.Brightness = trackBarBrightness.Value;
922 Properties.Settings.Default.Save();
923 iDisplay.SetBrightness(trackBarBrightness.Value);
929 /// CDS stands for Current Display Settings
931 private DisplaySettings cds
935 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
936 if (settings == null)
938 settings = new DisplaysSettings();
940 Properties.Settings.Default.DisplaysSettings = settings;
943 //Make sure all our settings have been created
944 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
946 settings.Displays.Add(new DisplaySettings());
949 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
950 return displaySettings;
955 /// Check if the given font has a fixed character pitch.
957 /// <param name="ft"></param>
958 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
959 public float IsFixedWidth(Font ft)
961 Graphics g = CreateGraphics();
962 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
963 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
965 bool fixedWidth = true;
967 foreach (char c in charSizes)
968 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
980 /// Synchronize UI with settings
982 private void UpdateStatus()
985 checkBoxShowBorders.Checked = cds.ShowBorders;
986 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
988 //Set the proper font to each of our labels
989 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
991 ctrl.Font = cds.Font;
995 //Check if "run on Windows startup" is enabled
996 checkBoxAutoStart.Checked = iStartupManager.Startup;
998 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
999 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
1000 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
1001 labelStartFileName.Text = Properties.Settings.Default.StartFileName;
1002 checkBoxReverseScreen.Checked = cds.ReverseScreen;
1003 checkBoxInverseColors.Checked = cds.InverseColors;
1004 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
1005 checkBoxScaleToFit.Checked = cds.ScaleToFit;
1006 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1007 labelMinFontSize.Enabled = cds.ScaleToFit;
1008 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
1009 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
1010 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
1011 timer.Interval = cds.TimerInterval;
1012 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
1013 textBoxScrollLoopSeparator.Text = cds.Separator;
1015 SetupPixelDelegates();
1017 if (iDisplay.IsOpen())
1019 //We have a display connection
1020 //Reflect that in our UI
1022 tableLayoutPanel.Enabled = true;
1023 panelDisplay.Enabled = true;
1025 //Only setup brightness if display is open
1026 trackBarBrightness.Minimum = iDisplay.MinBrightness();
1027 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
1028 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
1030 //Brightness out of range, this can occur when using auto-detect
1031 //Use max brightness instead
1032 trackBarBrightness.Value = iDisplay.MaxBrightness();
1033 iDisplay.SetBrightness(iDisplay.MaxBrightness());
1037 trackBarBrightness.Value = cds.Brightness;
1038 iDisplay.SetBrightness(cds.Brightness);
1041 //Try compute the steps to something that makes sense
1042 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
1043 trackBarBrightness.SmallChange = 1;
1046 buttonFill.Enabled = true;
1047 buttonClear.Enabled = true;
1048 buttonOpen.Enabled = false;
1049 buttonClose.Enabled = true;
1050 trackBarBrightness.Enabled = true;
1051 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1052 //+ " - " + iDisplay.SerialNumber();
1054 if (iDisplay.SupportPowerOnOff())
1056 buttonPowerOn.Enabled = true;
1057 buttonPowerOff.Enabled = true;
1061 buttonPowerOn.Enabled = false;
1062 buttonPowerOff.Enabled = false;
1065 if (iDisplay.SupportClock())
1067 buttonShowClock.Enabled = true;
1068 buttonHideClock.Enabled = true;
1072 buttonShowClock.Enabled = false;
1073 buttonHideClock.Enabled = false;
1077 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1078 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel)>0;
1080 if (cds.ShowVolumeLabel)
1082 iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
1086 iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
1091 //Display is connection not available
1092 //Reflect that in our UI
1093 checkBoxShowVolumeLabel.Enabled = false;
1094 tableLayoutPanel.Enabled = false;
1095 panelDisplay.Enabled = false;
1096 buttonFill.Enabled = false;
1097 buttonClear.Enabled = false;
1098 buttonOpen.Enabled = true;
1099 buttonClose.Enabled = false;
1100 trackBarBrightness.Enabled = false;
1101 buttonPowerOn.Enabled = false;
1102 buttonPowerOff.Enabled = false;
1103 buttonShowClock.Enabled = false;
1104 buttonHideClock.Enabled = false;
1105 toolStripStatusLabelConnect.Text = "Disconnected";
1106 toolStripStatusLabelPower.Text = "N/A";
1115 /// <param name="sender"></param>
1116 /// <param name="e"></param>
1117 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1119 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1120 Properties.Settings.Default.Save();
1124 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1126 //Save our show borders setting
1127 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1128 cds.ShowBorders = checkBoxShowBorders.Checked;
1129 Properties.Settings.Default.Save();
1133 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1135 //Save our connect on startup setting
1136 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1137 Properties.Settings.Default.Save();
1140 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1142 //Save our "Minimize to tray" setting
1143 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1144 Properties.Settings.Default.Save();
1148 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1150 //Save our "Start minimized" setting
1151 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1152 Properties.Settings.Default.Save();
1155 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1157 iStartupManager.Startup = checkBoxAutoStart.Checked;
1161 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1163 //Save our reverse screen setting
1164 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1165 Properties.Settings.Default.Save();
1166 SetupPixelDelegates();
1169 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1171 //Save our inverse colors setting
1172 cds.InverseColors = checkBoxInverseColors.Checked;
1173 Properties.Settings.Default.Save();
1174 SetupPixelDelegates();
1177 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1179 //Save our scale to fit setting
1180 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1181 Properties.Settings.Default.Save();
1183 labelMinFontSize.Enabled = cds.ScaleToFit;
1184 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1187 private void MainForm_Resize(object sender, EventArgs e)
1189 if (WindowState == FormWindowState.Minimized)
1192 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1193 iCreateBitmap = true;
1197 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1199 iNetworkManager.Dispose();
1200 CloseDisplayConnection();
1202 e.Cancel = iClosing;
1205 public void StartServer()
1207 iServiceHost = new ServiceHost
1210 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1213 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1214 iServiceHost.Open();
1217 public void StopServer()
1219 if (iClients.Count > 0 && !iClosing)
1223 BroadcastCloseEvent();
1227 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1229 iClosing = false; //We make sure we force close if asked twice
1234 //We removed that as it often lags for some reason
1235 //iServiceHost.Close();
1239 public void BroadcastCloseEvent()
1241 Trace.TraceInformation("BroadcastCloseEvent - start");
1243 var inactiveClients = new List<string>();
1244 foreach (var client in iClients)
1246 //if (client.Key != eventData.ClientName)
1250 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1251 client.Value.Callback.OnCloseOrder(/*eventData*/);
1253 catch (Exception ex)
1255 inactiveClients.Add(client.Key);
1260 if (inactiveClients.Count > 0)
1262 foreach (var client in inactiveClients)
1264 iClients.Remove(client);
1265 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1269 if (iClients.Count==0)
1276 /// Just remove all our fields.
1278 private void ClearLayout()
1280 tableLayoutPanel.Controls.Clear();
1281 tableLayoutPanel.RowStyles.Clear();
1282 tableLayoutPanel.ColumnStyles.Clear();
1283 iCurrentClientData = null;
1287 /// Just launch a demo client.
1289 private void StartNewClient(string aTopText = "", string aBottomText = "")
1291 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1292 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1293 clientThread.Start(myParams);
1297 private void buttonStartClient_Click(object sender, EventArgs e)
1302 private void buttonSuspend_Click(object sender, EventArgs e)
1304 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1305 timer.Enabled = !timer.Enabled;
1308 buttonSuspend.Text = "Run";
1312 buttonSuspend.Text = "Pause";
1316 private void buttonCloseClients_Click(object sender, EventArgs e)
1318 BroadcastCloseEvent();
1321 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1330 /// <param name="aSessionId"></param>
1331 /// <param name="aCallback"></param>
1332 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1334 if (this.InvokeRequired)
1336 //Not in the proper thread, invoke ourselves
1337 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1338 this.Invoke(d, new object[] { aSessionId, aCallback });
1342 //We are in the proper thread
1343 //Add this session to our collection of clients
1344 ClientData newClient = new ClientData(aSessionId, aCallback);
1345 Program.iMainForm.iClients.Add(aSessionId, newClient);
1346 //Add this session to our UI
1347 UpdateClientTreeViewNode(newClient);
1354 /// <param name="aSessionId"></param>
1355 public void RemoveClientThreadSafe(string aSessionId)
1357 if (this.InvokeRequired)
1359 //Not in the proper thread, invoke ourselves
1360 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1361 this.Invoke(d, new object[] { aSessionId });
1365 //We are in the proper thread
1366 //Remove this session from both client collection and UI tree view
1367 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1369 Program.iMainForm.iClients.Remove(aSessionId);
1370 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1373 if (iClients.Count == 0)
1375 //Clear our screen when last client disconnects
1380 //We were closing our form
1381 //All clients are now closed
1382 //Just resume our close operation
1393 /// <param name="aSessionId"></param>
1394 /// <param name="aLayout"></param>
1395 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1397 if (this.InvokeRequired)
1399 //Not in the proper thread, invoke ourselves
1400 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1401 this.Invoke(d, new object[] { aSessionId, aLayout });
1405 ClientData client = iClients[aSessionId];
1408 client.Layout = aLayout;
1409 UpdateTableLayoutPanel(client);
1411 UpdateClientTreeViewNode(client);
1419 /// <param name="aSessionId"></param>
1420 /// <param name="aField"></param>
1421 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1423 if (this.InvokeRequired)
1425 //Not in the proper thread, invoke ourselves
1426 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1427 this.Invoke(d, new object[] { aSessionId, aField });
1431 //We are in the proper thread
1432 //Call the non-thread-safe variant
1433 SetClientField(aSessionId, aField);
1440 /// <param name="aSessionId"></param>
1441 /// <param name="aField"></param>
1442 private void SetClientField(string aSessionId, DataField aField)
1444 SetCurrentClient(aSessionId);
1445 ClientData client = iClients[aSessionId];
1448 bool somethingChanged = false;
1450 //Make sure all our fields are in place
1451 while (client.Fields.Count < (aField.Index + 1))
1453 //Add a text field with proper index
1454 client.Fields.Add(new DataField(client.Fields.Count));
1455 somethingChanged = true;
1458 if (client.Fields[aField.Index].IsSameLayout(aField))
1460 //Same layout just update our field
1461 client.Fields[aField.Index] = aField;
1463 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1465 //Text field control already in place, just change the text
1466 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1467 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1468 label.Text = aField.Text;
1469 label.TextAlign = aField.Alignment;
1471 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1473 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1474 //Bitmap field control already in place just change the bitmap
1475 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1476 pictureBox.Image = aField.Bitmap;
1480 somethingChanged = true;
1481 //The requested control in our layout it not of the correct type
1482 //Wrong control type, re-create them all
1483 UpdateTableLayoutPanel(iCurrentClientData);
1488 somethingChanged = true;
1489 //Different layout, need to rebuild it
1490 client.Fields[aField.Index] = aField;
1491 UpdateTableLayoutPanel(iCurrentClientData);
1495 if (somethingChanged)
1497 UpdateClientTreeViewNode(client);
1505 /// <param name="aTexts"></param>
1506 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1508 if (this.InvokeRequired)
1510 //Not in the proper thread, invoke ourselves
1511 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1512 this.Invoke(d, new object[] { aSessionId, aFields });
1516 //Put each our text fields in a label control
1517 foreach (DataField field in aFields)
1519 SetClientField(aSessionId, field);
1527 /// <param name="aSessionId"></param>
1528 /// <param name="aName"></param>
1529 public void SetClientNameThreadSafe(string aSessionId, string aName)
1531 if (this.InvokeRequired)
1533 //Not in the proper thread, invoke ourselves
1534 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1535 this.Invoke(d, new object[] { aSessionId, aName });
1539 //We are in the proper thread
1541 ClientData client = iClients[aSessionId];
1545 client.Name = aName;
1546 //Update our tree-view
1547 UpdateClientTreeViewNode(client);
1555 /// <param name="aClient"></param>
1556 private void UpdateClientTreeViewNode(ClientData aClient)
1558 if (aClient == null)
1563 TreeNode node = null;
1564 //Check that our client node already exists
1565 //Get our client root node using its key which is our session ID
1566 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1567 if (nodes.Count()>0)
1569 //We already have a node for that client
1571 //Clear children as we are going to recreate them below
1576 //Client node does not exists create a new one
1577 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1578 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1584 if (aClient.Name != "")
1586 //We have a name, us it as text for our root node
1587 node.Text = aClient.Name;
1588 //Add a child with SessionId
1589 node.Nodes.Add(new TreeNode(aClient.SessionId));
1593 //No name, use session ID instead
1594 node.Text = aClient.SessionId;
1597 if (aClient.Fields.Count > 0)
1599 //Create root node for our texts
1600 TreeNode textsRoot = new TreeNode("Fields");
1601 node.Nodes.Add(textsRoot);
1602 //For each text add a new entry
1603 foreach (DataField field in aClient.Fields)
1605 if (!field.IsBitmap)
1607 DataField textField = (DataField)field;
1608 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1612 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1622 /// Update our table layout row styles to make sure each rows have similar height
1624 private void UpdateTableLayoutRowStyles()
1626 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1628 rowStyle.SizeType = SizeType.Percent;
1629 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1634 /// Update our display table layout.
1636 /// <param name="aLayout"></param>
1637 private void UpdateTableLayoutPanel(ClientData aClient)
1639 if (aClient == null)
1646 TableLayout layout = aClient.Layout;
1649 tableLayoutPanel.Controls.Clear();
1650 tableLayoutPanel.RowStyles.Clear();
1651 tableLayoutPanel.ColumnStyles.Clear();
1652 tableLayoutPanel.RowCount = 0;
1653 tableLayoutPanel.ColumnCount = 0;
1655 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1657 tableLayoutPanel.RowCount++;
1660 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1662 tableLayoutPanel.ColumnCount++;
1665 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1667 //Create our column styles
1668 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1670 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1674 //Create our row styles
1675 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1678 //Check if we already have a control
1679 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1680 if (existingControl!=null)
1682 //We already have a control in that cell as a results of row/col spanning
1683 //Move on to next cell then
1689 //Check if a client field already exists for that cell
1690 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1692 //No client field specified, create a text field by default
1693 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1696 //Create a control corresponding to the field specified for that cell
1697 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1698 Control control = CreateControlForDataField(field);
1700 //Add newly created control to our table layout at the specified row and column
1701 tableLayoutPanel.Controls.Add(control, i, j);
1702 //Make sure we specify row and column span for that new control
1703 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1704 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1709 while (aClient.Fields.Count > fieldCount)
1711 //We have too much fields for this layout
1712 //Just discard them until we get there
1713 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1720 /// Check our type of data field and create corresponding control
1722 /// <param name="aField"></param>
1723 private Control CreateControlForDataField(DataField aField)
1725 Control control=null;
1726 if (!aField.IsBitmap)
1728 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1729 label.AutoEllipsis = true;
1730 label.AutoSize = true;
1731 label.BackColor = System.Drawing.Color.Transparent;
1732 label.Dock = System.Windows.Forms.DockStyle.Fill;
1733 label.Location = new System.Drawing.Point(1, 1);
1734 label.Margin = new System.Windows.Forms.Padding(0);
1735 label.Name = "marqueeLabel" + aField.Index;
1736 label.OwnTimer = false;
1737 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1738 label.Separator = cds.Separator;
1739 label.MinFontSize = cds.MinFontSize;
1740 label.ScaleToFit = cds.ScaleToFit;
1741 //control.Size = new System.Drawing.Size(254, 30);
1742 //control.TabIndex = 2;
1743 label.Font = cds.Font;
1745 label.TextAlign = aField.Alignment;
1746 label.UseCompatibleTextRendering = true;
1747 label.Text = aField.Text;
1753 //Create picture box
1754 PictureBox picture = new PictureBox();
1755 picture.AutoSize = true;
1756 picture.BackColor = System.Drawing.Color.Transparent;
1757 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1758 picture.Location = new System.Drawing.Point(1, 1);
1759 picture.Margin = new System.Windows.Forms.Padding(0);
1760 picture.Name = "pictureBox" + aField;
1762 picture.Image = aField.Bitmap;
1771 /// Called when the user selected a new display type.
1773 /// <param name="sender"></param>
1774 /// <param name="e"></param>
1775 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1777 //Store the selected display type in our settings
1778 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1779 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1780 Properties.Settings.Default.Save();
1782 //Try re-opening the display connection if we were already connected.
1783 //Otherwise just update our status to reflect display type change.
1784 if (iDisplay.IsOpen())
1786 OpenDisplayConnection();
1794 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1796 if (maskedTextBoxTimerInterval.Text != "")
1798 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1802 timer.Interval = interval;
1803 cds.TimerInterval = timer.Interval;
1804 Properties.Settings.Default.Save();
1809 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1811 if (maskedTextBoxMinFontSize.Text != "")
1813 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1815 if (minFontSize > 0)
1817 cds.MinFontSize = minFontSize;
1818 Properties.Settings.Default.Save();
1819 //We need to recreate our layout for that change to take effect
1820 UpdateTableLayoutPanel(iCurrentClientData);
1826 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1828 if (maskedTextBoxScrollingSpeed.Text != "")
1830 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1832 if (scrollingSpeed > 0)
1834 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1835 Properties.Settings.Default.Save();
1836 //We need to recreate our layout for that change to take effect
1837 UpdateTableLayoutPanel(iCurrentClientData);
1842 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1844 cds.Separator = textBoxScrollLoopSeparator.Text;
1845 Properties.Settings.Default.Save();
1847 //Update our text fields
1848 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1850 ctrl.Separator = cds.Separator;
1855 private void buttonPowerOn_Click(object sender, EventArgs e)
1860 private void buttonPowerOff_Click(object sender, EventArgs e)
1862 iDisplay.PowerOff();
1865 private void buttonShowClock_Click(object sender, EventArgs e)
1870 private void buttonHideClock_Click(object sender, EventArgs e)
1875 private void buttonUpdate_Click(object sender, EventArgs e)
1877 InstallUpdateSyncWithInfo();
1885 if (!iDisplay.IsOpen())
1890 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1891 iSkipFrameRendering = true;
1894 iDisplay.SwapBuffers();
1895 //Then show our clock
1896 iDisplay.ShowClock();
1904 if (!iDisplay.IsOpen())
1909 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1910 iSkipFrameRendering = false;
1911 iDisplay.HideClock();
1914 private void InstallUpdateSyncWithInfo()
1916 UpdateCheckInfo info = null;
1918 if (ApplicationDeployment.IsNetworkDeployed)
1920 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1924 info = ad.CheckForDetailedUpdate();
1927 catch (DeploymentDownloadException dde)
1929 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);
1932 catch (InvalidDeploymentException ide)
1934 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);
1937 catch (InvalidOperationException ioe)
1939 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1943 if (info.UpdateAvailable)
1945 Boolean doUpdate = true;
1947 if (!info.IsUpdateRequired)
1949 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1950 if (!(DialogResult.OK == dr))
1957 // Display a message that the app MUST reboot. Display the minimum required version.
1958 MessageBox.Show("This application has detected a mandatory update from your current " +
1959 "version to version " + info.MinimumRequiredVersion.ToString() +
1960 ". The application will now install the update and restart.",
1961 "Update Available", MessageBoxButtons.OK,
1962 MessageBoxIcon.Information);
1970 MessageBox.Show("The application has been upgraded, and will now restart.");
1971 Application.Restart();
1973 catch (DeploymentDownloadException dde)
1975 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1982 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1991 private void SysTrayHideShow()
1997 WindowState = FormWindowState.Normal;
2002 /// Use to handle minimize events.
2004 /// <param name="sender"></param>
2005 /// <param name="e"></param>
2006 private void MainForm_SizeChanged(object sender, EventArgs e)
2008 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2021 /// <param name="sender"></param>
2022 /// <param name="e"></param>
2023 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2025 //Our table layout size has changed which means our display size has changed.
2026 //We need to re-create our bitmap.
2027 iCreateBitmap = true;
2033 /// <param name="sender"></param>
2034 /// <param name="e"></param>
2035 private void buttonSelectFile_Click(object sender, EventArgs e)
2037 //openFileDialog1.InitialDirectory = "c:\\";
2038 //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
2039 //openFileDialog.FilterIndex = 1;
2040 openFileDialog.RestoreDirectory = true;
2042 if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
2044 labelStartFileName.Text = openFileDialog.FileName;
2045 Properties.Settings.Default.StartFileName = openFileDialog.FileName;
2046 Properties.Settings.Default.Save();
2052 /// A UI thread copy of a client relevant data.
2053 /// Keeping this copy in the UI thread helps us deal with threading issues.
2055 public class ClientData
2057 public ClientData(string aSessionId, ICallback aCallback)
2059 SessionId = aSessionId;
2061 Fields = new List<DataField>();
2062 Layout = new TableLayout(1, 2); //Default to one column and two rows
2063 Callback = aCallback;
2066 public string SessionId { get; set; }
2067 public string Name { get; set; }
2068 public List<DataField> Fields { get; set; }
2069 public TableLayout Layout { get; set; }
2070 public ICallback Callback { get; set; }