Prevents switching clients too often.
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, bool aForce=false)
560 if (aSessionId == iCurrentClientSessionId)
562 //Given client is already the current one.
563 //Don't bother changing anything then.
568 //Check when was the last time we switched to that client
569 double lastSwitchToClientSecondsAgo = (DateTime.Now - iClients[aSessionId].LastSwitchTime).TotalSeconds;
570 //TODO: put that hard coded delay in settings
571 if (!aForce && (lastSwitchToClientSecondsAgo < 10))
573 //Don't switch clients too often
577 //Set current client ID.
578 iCurrentClientSessionId = aSessionId;
579 //Set the time we last switched to that client
580 iClients[aSessionId].LastSwitchTime = DateTime.Now;
581 //Fetch and set current client data.
582 iCurrentClientData = iClients[aSessionId];
583 //Apply layout and set data fields.
584 UpdateTableLayoutPanel(iCurrentClientData);
587 private void buttonFont_Click(object sender, EventArgs e)
589 //fontDialog.ShowColor = true;
590 //fontDialog.ShowApply = true;
591 fontDialog.ShowEffects = true;
592 fontDialog.Font = cds.Font;
594 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
596 //fontDialog.ShowHelp = true;
598 //fontDlg.MaxSize = 40;
599 //fontDlg.MinSize = 22;
601 //fontDialog.Parent = this;
602 //fontDialog.StartPosition = FormStartPosition.CenterParent;
604 //DlgBox.ShowDialog(fontDialog);
606 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
607 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
609 //Set the fonts to all our labels in our layout
610 foreach (Control ctrl in tableLayoutPanel.Controls)
612 if (ctrl is MarqueeLabel)
614 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
619 cds.Font = fontDialog.Font;
620 Properties.Settings.Default.Save();
629 void CheckFontHeight()
631 //Show font height and width
632 labelFontHeight.Text = "Font height: " + cds.Font.Height;
633 float charWidth = IsFixedWidth(cds.Font);
634 if (charWidth == 0.0f)
636 labelFontWidth.Visible = false;
640 labelFontWidth.Visible = true;
641 labelFontWidth.Text = "Font width: " + charWidth;
644 MarqueeLabel label = null;
645 //Get the first label control we can find
646 foreach (Control ctrl in tableLayoutPanel.Controls)
648 if (ctrl is MarqueeLabel)
650 label = (MarqueeLabel)ctrl;
655 //Now check font height and show a warning if needed.
656 if (label != null && label.Font.Height > label.Height)
658 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
659 labelWarning.Visible = true;
663 labelWarning.Visible = false;
668 private void buttonCapture_Click(object sender, EventArgs e)
670 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
671 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
672 //Bitmap bmpToSave = new Bitmap(bmp);
673 bmp.Save("D:\\capture.png");
675 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
678 string outputFileName = "d:\\capture.png";
679 using (MemoryStream memory = new MemoryStream())
681 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
683 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
684 byte[] bytes = memory.ToArray();
685 fs.Write(bytes, 0, bytes.Length);
692 private void CheckForRequestResults()
694 if (iDisplay.IsRequestPending())
696 switch (iDisplay.AttemptRequestCompletion())
698 case MiniDisplay.Request.FirmwareRevision:
699 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
700 //Issue next request then
701 iDisplay.RequestPowerSupplyStatus();
704 case MiniDisplay.Request.PowerSupplyStatus:
705 if (iDisplay.PowerSupplyStatus())
707 toolStripStatusLabelPower.Text = "ON";
711 toolStripStatusLabelPower.Text = "OFF";
713 //Issue next request then
714 iDisplay.RequestDeviceId();
717 case MiniDisplay.Request.DeviceId:
718 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
719 //No more request to issue
725 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
727 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
734 public static uint ColorUntouched(int aX, int aY, uint aPixel)
739 public static uint ColorInversed(int aX, int aY, uint aPixel)
744 public static uint ColorChessboard(int aX, int aY, uint aPixel)
746 if ((aX % 2 == 0) && (aY % 2 == 0))
750 else if ((aX % 2 != 0) && (aY % 2 != 0))
758 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
760 return aBmp.Width - aX - 1;
763 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
765 return iBmp.Height - aY - 1;
768 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
773 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
779 /// Select proper pixel delegates according to our current settings.
781 private void SetupPixelDelegates()
783 //Select our pixel processing routine
784 if (cds.InverseColors)
786 //iColorFx = ColorChessboard;
787 iColorFx = ColorInversed;
791 iColorFx = ColorWhiteIsOn;
794 //Select proper coordinate translation functions
795 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
796 if (cds.ReverseScreen)
798 iScreenX = ScreenReversedX;
799 iScreenY = ScreenReversedY;
809 //This is our timer tick responsible to perform our render
810 private void timer_Tick(object sender, EventArgs e)
812 //Update our animations
813 DateTime NewTickTime = DateTime.Now;
815 UpdateNetworkSignal(LastTickTime, NewTickTime);
817 //Update animation for all our marquees
818 foreach (Control ctrl in tableLayoutPanel.Controls)
820 if (ctrl is MarqueeLabel)
822 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
827 if (iDisplay.IsOpen())
829 CheckForRequestResults();
831 //Check if frame rendering is needed
832 //Typically used when showing clock
833 if (!iSkipFrameRendering)
838 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
840 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
841 //iBmp.Save("D:\\capture.png");
843 //Send it to our display
844 for (int i = 0; i < iBmp.Width; i++)
846 for (int j = 0; j < iBmp.Height; j++)
850 //Get our processed pixel coordinates
851 int x = iScreenX(iBmp, i);
852 int y = iScreenY(iBmp, j);
854 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
855 //Apply color effects
856 color = iColorFx(x, y, color);
858 iDisplay.SetPixel(x, y, color);
863 iDisplay.SwapBuffers();
867 //Compute instant FPS
868 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
870 LastTickTime = NewTickTime;
875 /// Attempt to establish connection with our display hardware.
877 private void OpenDisplayConnection()
879 CloseDisplayConnection();
881 if (!iDisplay.Open((MiniDisplay.Type)cds.DisplayType))
884 toolStripStatusLabelConnect.Text = "Connection error";
888 private void CloseDisplayConnection()
890 //Status will be updated upon receiving the closed event
892 if (iDisplay == null || !iDisplay.IsOpen())
897 //Do not clear if we gave up on rendering already.
898 //This means we will keep on displaying clock on MDM166AA for instance.
899 if (!iSkipFrameRendering)
902 iDisplay.SwapBuffers();
905 iDisplay.SetAllIconsStatus(0); //Turn off all icons
909 private void buttonOpen_Click(object sender, EventArgs e)
911 OpenDisplayConnection();
914 private void buttonClose_Click(object sender, EventArgs e)
916 CloseDisplayConnection();
919 private void buttonClear_Click(object sender, EventArgs e)
922 iDisplay.SwapBuffers();
925 private void buttonFill_Click(object sender, EventArgs e)
928 iDisplay.SwapBuffers();
931 private void trackBarBrightness_Scroll(object sender, EventArgs e)
933 cds.Brightness = trackBarBrightness.Value;
934 Properties.Settings.Default.Save();
935 iDisplay.SetBrightness(trackBarBrightness.Value);
941 /// CDS stands for Current Display Settings
943 private DisplaySettings cds
947 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
948 if (settings == null)
950 settings = new DisplaysSettings();
952 Properties.Settings.Default.DisplaysSettings = settings;
955 //Make sure all our settings have been created
956 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
958 settings.Displays.Add(new DisplaySettings());
961 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
962 return displaySettings;
967 /// Check if the given font has a fixed character pitch.
969 /// <param name="ft"></param>
970 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
971 public float IsFixedWidth(Font ft)
973 Graphics g = CreateGraphics();
974 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
975 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
977 bool fixedWidth = true;
979 foreach (char c in charSizes)
980 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
992 /// Synchronize UI with settings
994 private void UpdateStatus()
997 checkBoxShowBorders.Checked = cds.ShowBorders;
998 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1000 //Set the proper font to each of our labels
1001 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1003 ctrl.Font = cds.Font;
1007 //Check if "run on Windows startup" is enabled
1008 checkBoxAutoStart.Checked = iStartupManager.Startup;
1010 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
1011 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
1012 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
1013 labelStartFileName.Text = Properties.Settings.Default.StartFileName;
1014 checkBoxReverseScreen.Checked = cds.ReverseScreen;
1015 checkBoxInverseColors.Checked = cds.InverseColors;
1016 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
1017 checkBoxScaleToFit.Checked = cds.ScaleToFit;
1018 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1019 labelMinFontSize.Enabled = cds.ScaleToFit;
1020 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
1021 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
1022 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
1023 timer.Interval = cds.TimerInterval;
1024 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
1025 textBoxScrollLoopSeparator.Text = cds.Separator;
1027 SetupPixelDelegates();
1029 if (iDisplay.IsOpen())
1031 //We have a display connection
1032 //Reflect that in our UI
1034 tableLayoutPanel.Enabled = true;
1035 panelDisplay.Enabled = true;
1037 //Only setup brightness if display is open
1038 trackBarBrightness.Minimum = iDisplay.MinBrightness();
1039 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
1040 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
1042 //Brightness out of range, this can occur when using auto-detect
1043 //Use max brightness instead
1044 trackBarBrightness.Value = iDisplay.MaxBrightness();
1045 iDisplay.SetBrightness(iDisplay.MaxBrightness());
1049 trackBarBrightness.Value = cds.Brightness;
1050 iDisplay.SetBrightness(cds.Brightness);
1053 //Try compute the steps to something that makes sense
1054 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
1055 trackBarBrightness.SmallChange = 1;
1058 buttonFill.Enabled = true;
1059 buttonClear.Enabled = true;
1060 buttonOpen.Enabled = false;
1061 buttonClose.Enabled = true;
1062 trackBarBrightness.Enabled = true;
1063 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1064 //+ " - " + iDisplay.SerialNumber();
1066 if (iDisplay.SupportPowerOnOff())
1068 buttonPowerOn.Enabled = true;
1069 buttonPowerOff.Enabled = true;
1073 buttonPowerOn.Enabled = false;
1074 buttonPowerOff.Enabled = false;
1077 if (iDisplay.SupportClock())
1079 buttonShowClock.Enabled = true;
1080 buttonHideClock.Enabled = true;
1084 buttonShowClock.Enabled = false;
1085 buttonHideClock.Enabled = false;
1089 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1090 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel)>0;
1092 if (cds.ShowVolumeLabel)
1094 iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
1098 iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
1103 //Display is connection not available
1104 //Reflect that in our UI
1105 checkBoxShowVolumeLabel.Enabled = false;
1106 tableLayoutPanel.Enabled = false;
1107 panelDisplay.Enabled = false;
1108 buttonFill.Enabled = false;
1109 buttonClear.Enabled = false;
1110 buttonOpen.Enabled = true;
1111 buttonClose.Enabled = false;
1112 trackBarBrightness.Enabled = false;
1113 buttonPowerOn.Enabled = false;
1114 buttonPowerOff.Enabled = false;
1115 buttonShowClock.Enabled = false;
1116 buttonHideClock.Enabled = false;
1117 toolStripStatusLabelConnect.Text = "Disconnected";
1118 toolStripStatusLabelPower.Text = "N/A";
1127 /// <param name="sender"></param>
1128 /// <param name="e"></param>
1129 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1131 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1132 Properties.Settings.Default.Save();
1136 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1138 //Save our show borders setting
1139 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1140 cds.ShowBorders = checkBoxShowBorders.Checked;
1141 Properties.Settings.Default.Save();
1145 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1147 //Save our connect on startup setting
1148 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1149 Properties.Settings.Default.Save();
1152 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1154 //Save our "Minimize to tray" setting
1155 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1156 Properties.Settings.Default.Save();
1160 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1162 //Save our "Start minimized" setting
1163 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1164 Properties.Settings.Default.Save();
1167 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1169 iStartupManager.Startup = checkBoxAutoStart.Checked;
1173 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1175 //Save our reverse screen setting
1176 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1177 Properties.Settings.Default.Save();
1178 SetupPixelDelegates();
1181 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1183 //Save our inverse colors setting
1184 cds.InverseColors = checkBoxInverseColors.Checked;
1185 Properties.Settings.Default.Save();
1186 SetupPixelDelegates();
1189 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1191 //Save our scale to fit setting
1192 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1193 Properties.Settings.Default.Save();
1195 labelMinFontSize.Enabled = cds.ScaleToFit;
1196 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1199 private void MainForm_Resize(object sender, EventArgs e)
1201 if (WindowState == FormWindowState.Minimized)
1204 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1205 iCreateBitmap = true;
1209 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1211 iNetworkManager.Dispose();
1212 CloseDisplayConnection();
1214 e.Cancel = iClosing;
1217 public void StartServer()
1219 iServiceHost = new ServiceHost
1222 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1225 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1226 iServiceHost.Open();
1229 public void StopServer()
1231 if (iClients.Count > 0 && !iClosing)
1235 BroadcastCloseEvent();
1239 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1241 iClosing = false; //We make sure we force close if asked twice
1246 //We removed that as it often lags for some reason
1247 //iServiceHost.Close();
1251 public void BroadcastCloseEvent()
1253 Trace.TraceInformation("BroadcastCloseEvent - start");
1255 var inactiveClients = new List<string>();
1256 foreach (var client in iClients)
1258 //if (client.Key != eventData.ClientName)
1262 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1263 client.Value.Callback.OnCloseOrder(/*eventData*/);
1265 catch (Exception ex)
1267 inactiveClients.Add(client.Key);
1272 if (inactiveClients.Count > 0)
1274 foreach (var client in inactiveClients)
1276 iClients.Remove(client);
1277 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1281 if (iClients.Count==0)
1288 /// Just remove all our fields.
1290 private void ClearLayout()
1292 tableLayoutPanel.Controls.Clear();
1293 tableLayoutPanel.RowStyles.Clear();
1294 tableLayoutPanel.ColumnStyles.Clear();
1295 iCurrentClientData = null;
1299 /// Just launch a demo client.
1301 private void StartNewClient(string aTopText = "", string aBottomText = "")
1303 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1304 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1305 clientThread.Start(myParams);
1309 private void buttonStartClient_Click(object sender, EventArgs e)
1314 private void buttonSuspend_Click(object sender, EventArgs e)
1316 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1317 timer.Enabled = !timer.Enabled;
1320 buttonSuspend.Text = "Run";
1324 buttonSuspend.Text = "Pause";
1328 private void buttonCloseClients_Click(object sender, EventArgs e)
1330 BroadcastCloseEvent();
1333 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1335 //Root node must have at least one child
1336 if (e.Node.Nodes.Count == 0)
1341 //If the selected node is the root node of a client then switch to it
1342 string sessionId=e.Node.Nodes[0].Text; //First child of a root node is the sessionId
1343 if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at
1345 //We have a valid session just switch to that client
1346 SetCurrentClient(sessionId,true);
1355 /// <param name="aSessionId"></param>
1356 /// <param name="aCallback"></param>
1357 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1359 if (this.InvokeRequired)
1361 //Not in the proper thread, invoke ourselves
1362 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1363 this.Invoke(d, new object[] { aSessionId, aCallback });
1367 //We are in the proper thread
1368 //Add this session to our collection of clients
1369 ClientData newClient = new ClientData(aSessionId, aCallback);
1370 Program.iMainForm.iClients.Add(aSessionId, newClient);
1371 //Add this session to our UI
1372 UpdateClientTreeViewNode(newClient);
1379 /// <param name="aSessionId"></param>
1380 public void RemoveClientThreadSafe(string aSessionId)
1382 if (this.InvokeRequired)
1384 //Not in the proper thread, invoke ourselves
1385 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1386 this.Invoke(d, new object[] { aSessionId });
1390 //We are in the proper thread
1391 //Remove this session from both client collection and UI tree view
1392 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1394 Program.iMainForm.iClients.Remove(aSessionId);
1395 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1398 if (iClients.Count == 0)
1400 //Clear our screen when last client disconnects
1405 //We were closing our form
1406 //All clients are now closed
1407 //Just resume our close operation
1418 /// <param name="aSessionId"></param>
1419 /// <param name="aLayout"></param>
1420 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1422 if (this.InvokeRequired)
1424 //Not in the proper thread, invoke ourselves
1425 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1426 this.Invoke(d, new object[] { aSessionId, aLayout });
1430 ClientData client = iClients[aSessionId];
1433 //Set our client layout then
1434 client.Layout = aLayout;
1436 UpdateClientTreeViewNode(client);
1444 /// <param name="aSessionId"></param>
1445 /// <param name="aField"></param>
1446 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1448 if (this.InvokeRequired)
1450 //Not in the proper thread, invoke ourselves
1451 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1452 this.Invoke(d, new object[] { aSessionId, aField });
1456 //We are in the proper thread
1457 //Call the non-thread-safe variant
1458 SetClientField(aSessionId, aField);
1465 /// <param name="aSessionId"></param>
1466 /// <param name="aField"></param>
1467 private void SetClientField(string aSessionId, DataField aField)
1469 //TODO: should check if the field actually changed?
1471 ClientData client = iClients[aSessionId];
1472 bool layoutChanged = false;
1474 //Make sure all our fields are in place
1475 while (client.Fields.Count < (aField.Index + 1))
1477 //Add a text field with proper index
1478 client.Fields.Add(new DataField(client.Fields.Count));
1479 layoutChanged = true;
1482 if (client.Fields[aField.Index].IsSameLayout(aField))
1484 //Same layout just update our field
1485 client.Fields[aField.Index] = aField;
1486 //If we are updating a field in our current client we need to update it in our panel
1487 if (aSessionId == iCurrentClientSessionId)
1489 if (aField.IsText && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1491 //Text field control already in place, just change the text
1492 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1493 layoutChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1494 label.Text = aField.Text;
1495 label.TextAlign = aField.Alignment;
1497 else if (aField.IsBitmap && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1499 layoutChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1500 //Bitmap field control already in place just change the bitmap
1501 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1502 pictureBox.Image = aField.Bitmap;
1506 layoutChanged = true;
1512 layoutChanged = true;
1513 //Different layout, need to rebuild it
1514 client.Fields[aField.Index] = aField;
1520 UpdateClientTreeViewNode(client);
1521 //Our layout has changed, if we are already the current client we need to update our panel
1522 if (aSessionId == iCurrentClientSessionId)
1524 //Apply layout and set data fields.
1525 UpdateTableLayoutPanel(iCurrentClientData);
1529 //When a client field is set we try switching to this client to present the new information to our user
1530 SetCurrentClient(aSessionId);
1536 /// <param name="aTexts"></param>
1537 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1539 if (this.InvokeRequired)
1541 //Not in the proper thread, invoke ourselves
1542 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1543 this.Invoke(d, new object[] { aSessionId, aFields });
1547 //Put each our text fields in a label control
1548 foreach (DataField field in aFields)
1550 SetClientField(aSessionId, field);
1558 /// <param name="aSessionId"></param>
1559 /// <param name="aName"></param>
1560 public void SetClientNameThreadSafe(string aSessionId, string aName)
1562 if (this.InvokeRequired)
1564 //Not in the proper thread, invoke ourselves
1565 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1566 this.Invoke(d, new object[] { aSessionId, aName });
1570 //We are in the proper thread
1572 ClientData client = iClients[aSessionId];
1576 client.Name = aName;
1577 //Update our tree-view
1578 UpdateClientTreeViewNode(client);
1586 /// <param name="aClient"></param>
1587 private void UpdateClientTreeViewNode(ClientData aClient)
1589 if (aClient == null)
1594 TreeNode node = null;
1595 //Check that our client node already exists
1596 //Get our client root node using its key which is our session ID
1597 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1598 if (nodes.Count()>0)
1600 //We already have a node for that client
1602 //Clear children as we are going to recreate them below
1607 //Client node does not exists create a new one
1608 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1609 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1615 if (aClient.Name != "")
1617 //We have a name, us it as text for our root node
1618 node.Text = aClient.Name;
1619 //Add a child with SessionId
1620 node.Nodes.Add(new TreeNode(aClient.SessionId));
1624 //No name, use session ID instead
1625 node.Text = aClient.SessionId;
1628 if (aClient.Fields.Count > 0)
1630 //Create root node for our texts
1631 TreeNode textsRoot = new TreeNode("Fields");
1632 node.Nodes.Add(textsRoot);
1633 //For each text add a new entry
1634 foreach (DataField field in aClient.Fields)
1636 if (!field.IsBitmap)
1638 DataField textField = (DataField)field;
1639 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1643 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1653 /// Update our table layout row styles to make sure each rows have similar height
1655 private void UpdateTableLayoutRowStyles()
1657 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1659 rowStyle.SizeType = SizeType.Percent;
1660 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1665 /// Update our display table layout.
1667 /// <param name="aLayout"></param>
1668 private void UpdateTableLayoutPanel(ClientData aClient)
1670 if (aClient == null)
1677 TableLayout layout = aClient.Layout;
1680 //First clean our current panel
1681 tableLayoutPanel.Controls.Clear();
1682 tableLayoutPanel.RowStyles.Clear();
1683 tableLayoutPanel.ColumnStyles.Clear();
1684 tableLayoutPanel.RowCount = 0;
1685 tableLayoutPanel.ColumnCount = 0;
1687 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1689 tableLayoutPanel.RowCount++;
1692 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1694 tableLayoutPanel.ColumnCount++;
1697 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1699 //Create our column styles
1700 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1702 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1706 //Create our row styles
1707 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1710 //Check if we already have a control
1711 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1712 if (existingControl!=null)
1714 //We already have a control in that cell as a results of row/col spanning
1715 //Move on to next cell then
1721 //Check if a client field already exists for that cell
1722 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1724 //No client field specified, create a text field by default
1725 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1728 //Create a control corresponding to the field specified for that cell
1729 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1730 Control control = CreateControlForDataField(field);
1732 //Add newly created control to our table layout at the specified row and column
1733 tableLayoutPanel.Controls.Add(control, i, j);
1734 //Make sure we specify row and column span for that new control
1735 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1736 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1741 while (aClient.Fields.Count > fieldCount)
1743 //We have too much fields for this layout
1744 //Just discard them until we get there
1745 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1752 /// Check our type of data field and create corresponding control
1754 /// <param name="aField"></param>
1755 private Control CreateControlForDataField(DataField aField)
1757 Control control=null;
1758 if (!aField.IsBitmap)
1760 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1761 label.AutoEllipsis = true;
1762 label.AutoSize = true;
1763 label.BackColor = System.Drawing.Color.Transparent;
1764 label.Dock = System.Windows.Forms.DockStyle.Fill;
1765 label.Location = new System.Drawing.Point(1, 1);
1766 label.Margin = new System.Windows.Forms.Padding(0);
1767 label.Name = "marqueeLabel" + aField.Index;
1768 label.OwnTimer = false;
1769 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1770 label.Separator = cds.Separator;
1771 label.MinFontSize = cds.MinFontSize;
1772 label.ScaleToFit = cds.ScaleToFit;
1773 //control.Size = new System.Drawing.Size(254, 30);
1774 //control.TabIndex = 2;
1775 label.Font = cds.Font;
1777 label.TextAlign = aField.Alignment;
1778 label.UseCompatibleTextRendering = true;
1779 label.Text = aField.Text;
1785 //Create picture box
1786 PictureBox picture = new PictureBox();
1787 picture.AutoSize = true;
1788 picture.BackColor = System.Drawing.Color.Transparent;
1789 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1790 picture.Location = new System.Drawing.Point(1, 1);
1791 picture.Margin = new System.Windows.Forms.Padding(0);
1792 picture.Name = "pictureBox" + aField;
1794 picture.Image = aField.Bitmap;
1803 /// Called when the user selected a new display type.
1805 /// <param name="sender"></param>
1806 /// <param name="e"></param>
1807 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1809 //Store the selected display type in our settings
1810 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1811 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1812 Properties.Settings.Default.Save();
1814 //Try re-opening the display connection if we were already connected.
1815 //Otherwise just update our status to reflect display type change.
1816 if (iDisplay.IsOpen())
1818 OpenDisplayConnection();
1826 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1828 if (maskedTextBoxTimerInterval.Text != "")
1830 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1834 timer.Interval = interval;
1835 cds.TimerInterval = timer.Interval;
1836 Properties.Settings.Default.Save();
1841 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1843 if (maskedTextBoxMinFontSize.Text != "")
1845 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1847 if (minFontSize > 0)
1849 cds.MinFontSize = minFontSize;
1850 Properties.Settings.Default.Save();
1851 //We need to recreate our layout for that change to take effect
1852 UpdateTableLayoutPanel(iCurrentClientData);
1858 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1860 if (maskedTextBoxScrollingSpeed.Text != "")
1862 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1864 if (scrollingSpeed > 0)
1866 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1867 Properties.Settings.Default.Save();
1868 //We need to recreate our layout for that change to take effect
1869 UpdateTableLayoutPanel(iCurrentClientData);
1874 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1876 cds.Separator = textBoxScrollLoopSeparator.Text;
1877 Properties.Settings.Default.Save();
1879 //Update our text fields
1880 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1882 ctrl.Separator = cds.Separator;
1887 private void buttonPowerOn_Click(object sender, EventArgs e)
1892 private void buttonPowerOff_Click(object sender, EventArgs e)
1894 iDisplay.PowerOff();
1897 private void buttonShowClock_Click(object sender, EventArgs e)
1902 private void buttonHideClock_Click(object sender, EventArgs e)
1907 private void buttonUpdate_Click(object sender, EventArgs e)
1909 InstallUpdateSyncWithInfo();
1917 if (!iDisplay.IsOpen())
1922 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1923 iSkipFrameRendering = true;
1926 iDisplay.SwapBuffers();
1927 //Then show our clock
1928 iDisplay.ShowClock();
1936 if (!iDisplay.IsOpen())
1941 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1942 iSkipFrameRendering = false;
1943 iDisplay.HideClock();
1946 private void InstallUpdateSyncWithInfo()
1948 UpdateCheckInfo info = null;
1950 if (ApplicationDeployment.IsNetworkDeployed)
1952 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1956 info = ad.CheckForDetailedUpdate();
1959 catch (DeploymentDownloadException dde)
1961 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);
1964 catch (InvalidDeploymentException ide)
1966 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);
1969 catch (InvalidOperationException ioe)
1971 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1975 if (info.UpdateAvailable)
1977 Boolean doUpdate = true;
1979 if (!info.IsUpdateRequired)
1981 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1982 if (!(DialogResult.OK == dr))
1989 // Display a message that the app MUST reboot. Display the minimum required version.
1990 MessageBox.Show("This application has detected a mandatory update from your current " +
1991 "version to version " + info.MinimumRequiredVersion.ToString() +
1992 ". The application will now install the update and restart.",
1993 "Update Available", MessageBoxButtons.OK,
1994 MessageBoxIcon.Information);
2002 MessageBox.Show("The application has been upgraded, and will now restart.");
2003 Application.Restart();
2005 catch (DeploymentDownloadException dde)
2007 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
2014 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
2023 private void SysTrayHideShow()
2029 WindowState = FormWindowState.Normal;
2034 /// Use to handle minimize events.
2036 /// <param name="sender"></param>
2037 /// <param name="e"></param>
2038 private void MainForm_SizeChanged(object sender, EventArgs e)
2040 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2052 /// <param name="sender"></param>
2053 /// <param name="e"></param>
2054 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2056 //Our table layout size has changed which means our display size has changed.
2057 //We need to re-create our bitmap.
2058 iCreateBitmap = true;
2064 /// <param name="sender"></param>
2065 /// <param name="e"></param>
2066 private void buttonSelectFile_Click(object sender, EventArgs e)
2068 //openFileDialog1.InitialDirectory = "c:\\";
2069 //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
2070 //openFileDialog.FilterIndex = 1;
2071 openFileDialog.RestoreDirectory = true;
2073 if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
2075 labelStartFileName.Text = openFileDialog.FileName;
2076 Properties.Settings.Default.StartFileName = openFileDialog.FileName;
2077 Properties.Settings.Default.Save();