Fixing clients management.
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 if (iCurrentClientData != null)
571 double lastSwitchToClientSecondsAgo = (DateTime.Now - iCurrentClientData.LastSwitchTime).TotalSeconds;
572 //TODO: put that hard coded value as a client property
573 //Clients should be able to define how often they can be interrupted
574 //Thus a background client can set this to zero allowing any other client to interrupt at any time
575 //We could also compute this delay by looking at the requests frequencies?
576 if (!aForce && (lastSwitchToClientSecondsAgo < 30)) //Make sure a client is on for at least 30 seconds
578 //Don't switch clients too often
583 //Set current client ID.
584 iCurrentClientSessionId = aSessionId;
585 //Set the time we last switched to that client
586 iClients[aSessionId].LastSwitchTime = DateTime.Now;
587 //Fetch and set current client data.
588 iCurrentClientData = iClients[aSessionId];
589 //Apply layout and set data fields.
590 UpdateTableLayoutPanel(iCurrentClientData);
593 private void buttonFont_Click(object sender, EventArgs e)
595 //fontDialog.ShowColor = true;
596 //fontDialog.ShowApply = true;
597 fontDialog.ShowEffects = true;
598 fontDialog.Font = cds.Font;
600 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
602 //fontDialog.ShowHelp = true;
604 //fontDlg.MaxSize = 40;
605 //fontDlg.MinSize = 22;
607 //fontDialog.Parent = this;
608 //fontDialog.StartPosition = FormStartPosition.CenterParent;
610 //DlgBox.ShowDialog(fontDialog);
612 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
613 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
615 //Set the fonts to all our labels in our layout
616 foreach (Control ctrl in tableLayoutPanel.Controls)
618 if (ctrl is MarqueeLabel)
620 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
625 cds.Font = fontDialog.Font;
626 Properties.Settings.Default.Save();
635 void CheckFontHeight()
637 //Show font height and width
638 labelFontHeight.Text = "Font height: " + cds.Font.Height;
639 float charWidth = IsFixedWidth(cds.Font);
640 if (charWidth == 0.0f)
642 labelFontWidth.Visible = false;
646 labelFontWidth.Visible = true;
647 labelFontWidth.Text = "Font width: " + charWidth;
650 MarqueeLabel label = null;
651 //Get the first label control we can find
652 foreach (Control ctrl in tableLayoutPanel.Controls)
654 if (ctrl is MarqueeLabel)
656 label = (MarqueeLabel)ctrl;
661 //Now check font height and show a warning if needed.
662 if (label != null && label.Font.Height > label.Height)
664 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
665 labelWarning.Visible = true;
669 labelWarning.Visible = false;
674 private void buttonCapture_Click(object sender, EventArgs e)
676 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
677 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
678 //Bitmap bmpToSave = new Bitmap(bmp);
679 bmp.Save("D:\\capture.png");
681 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
684 string outputFileName = "d:\\capture.png";
685 using (MemoryStream memory = new MemoryStream())
687 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
689 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
690 byte[] bytes = memory.ToArray();
691 fs.Write(bytes, 0, bytes.Length);
698 private void CheckForRequestResults()
700 if (iDisplay.IsRequestPending())
702 switch (iDisplay.AttemptRequestCompletion())
704 case MiniDisplay.Request.FirmwareRevision:
705 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
706 //Issue next request then
707 iDisplay.RequestPowerSupplyStatus();
710 case MiniDisplay.Request.PowerSupplyStatus:
711 if (iDisplay.PowerSupplyStatus())
713 toolStripStatusLabelPower.Text = "ON";
717 toolStripStatusLabelPower.Text = "OFF";
719 //Issue next request then
720 iDisplay.RequestDeviceId();
723 case MiniDisplay.Request.DeviceId:
724 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
725 //No more request to issue
731 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
733 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
740 public static uint ColorUntouched(int aX, int aY, uint aPixel)
745 public static uint ColorInversed(int aX, int aY, uint aPixel)
750 public static uint ColorChessboard(int aX, int aY, uint aPixel)
752 if ((aX % 2 == 0) && (aY % 2 == 0))
756 else if ((aX % 2 != 0) && (aY % 2 != 0))
764 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
766 return aBmp.Width - aX - 1;
769 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
771 return iBmp.Height - aY - 1;
774 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
779 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
785 /// Select proper pixel delegates according to our current settings.
787 private void SetupPixelDelegates()
789 //Select our pixel processing routine
790 if (cds.InverseColors)
792 //iColorFx = ColorChessboard;
793 iColorFx = ColorInversed;
797 iColorFx = ColorWhiteIsOn;
800 //Select proper coordinate translation functions
801 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
802 if (cds.ReverseScreen)
804 iScreenX = ScreenReversedX;
805 iScreenY = ScreenReversedY;
815 //This is our timer tick responsible to perform our render
816 private void timer_Tick(object sender, EventArgs e)
818 //Update our animations
819 DateTime NewTickTime = DateTime.Now;
821 UpdateNetworkSignal(LastTickTime, NewTickTime);
823 //Update animation for all our marquees
824 foreach (Control ctrl in tableLayoutPanel.Controls)
826 if (ctrl is MarqueeLabel)
828 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
833 if (iDisplay.IsOpen())
835 CheckForRequestResults();
837 //Check if frame rendering is needed
838 //Typically used when showing clock
839 if (!iSkipFrameRendering)
844 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
846 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
847 //iBmp.Save("D:\\capture.png");
849 //Send it to our display
850 for (int i = 0; i < iBmp.Width; i++)
852 for (int j = 0; j < iBmp.Height; j++)
856 //Get our processed pixel coordinates
857 int x = iScreenX(iBmp, i);
858 int y = iScreenY(iBmp, j);
860 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
861 //Apply color effects
862 color = iColorFx(x, y, color);
864 iDisplay.SetPixel(x, y, color);
869 iDisplay.SwapBuffers();
873 //Compute instant FPS
874 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
876 LastTickTime = NewTickTime;
881 /// Attempt to establish connection with our display hardware.
883 private void OpenDisplayConnection()
885 CloseDisplayConnection();
887 if (!iDisplay.Open((MiniDisplay.Type)cds.DisplayType))
890 toolStripStatusLabelConnect.Text = "Connection error";
894 private void CloseDisplayConnection()
896 //Status will be updated upon receiving the closed event
898 if (iDisplay == null || !iDisplay.IsOpen())
903 //Do not clear if we gave up on rendering already.
904 //This means we will keep on displaying clock on MDM166AA for instance.
905 if (!iSkipFrameRendering)
908 iDisplay.SwapBuffers();
911 iDisplay.SetAllIconsStatus(0); //Turn off all icons
915 private void buttonOpen_Click(object sender, EventArgs e)
917 OpenDisplayConnection();
920 private void buttonClose_Click(object sender, EventArgs e)
922 CloseDisplayConnection();
925 private void buttonClear_Click(object sender, EventArgs e)
928 iDisplay.SwapBuffers();
931 private void buttonFill_Click(object sender, EventArgs e)
934 iDisplay.SwapBuffers();
937 private void trackBarBrightness_Scroll(object sender, EventArgs e)
939 cds.Brightness = trackBarBrightness.Value;
940 Properties.Settings.Default.Save();
941 iDisplay.SetBrightness(trackBarBrightness.Value);
947 /// CDS stands for Current Display Settings
949 private DisplaySettings cds
953 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
954 if (settings == null)
956 settings = new DisplaysSettings();
958 Properties.Settings.Default.DisplaysSettings = settings;
961 //Make sure all our settings have been created
962 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
964 settings.Displays.Add(new DisplaySettings());
967 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
968 return displaySettings;
973 /// Check if the given font has a fixed character pitch.
975 /// <param name="ft"></param>
976 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
977 public float IsFixedWidth(Font ft)
979 Graphics g = CreateGraphics();
980 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
981 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
983 bool fixedWidth = true;
985 foreach (char c in charSizes)
986 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
998 /// Synchronize UI with settings
1000 private void UpdateStatus()
1003 checkBoxShowBorders.Checked = cds.ShowBorders;
1004 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1006 //Set the proper font to each of our labels
1007 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1009 ctrl.Font = cds.Font;
1013 //Check if "run on Windows startup" is enabled
1014 checkBoxAutoStart.Checked = iStartupManager.Startup;
1016 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
1017 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
1018 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
1019 labelStartFileName.Text = Properties.Settings.Default.StartFileName;
1020 checkBoxReverseScreen.Checked = cds.ReverseScreen;
1021 checkBoxInverseColors.Checked = cds.InverseColors;
1022 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
1023 checkBoxScaleToFit.Checked = cds.ScaleToFit;
1024 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1025 labelMinFontSize.Enabled = cds.ScaleToFit;
1026 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
1027 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
1028 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
1029 timer.Interval = cds.TimerInterval;
1030 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
1031 textBoxScrollLoopSeparator.Text = cds.Separator;
1033 SetupPixelDelegates();
1035 if (iDisplay.IsOpen())
1037 //We have a display connection
1038 //Reflect that in our UI
1040 tableLayoutPanel.Enabled = true;
1041 panelDisplay.Enabled = true;
1043 //Only setup brightness if display is open
1044 trackBarBrightness.Minimum = iDisplay.MinBrightness();
1045 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
1046 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
1048 //Brightness out of range, this can occur when using auto-detect
1049 //Use max brightness instead
1050 trackBarBrightness.Value = iDisplay.MaxBrightness();
1051 iDisplay.SetBrightness(iDisplay.MaxBrightness());
1055 trackBarBrightness.Value = cds.Brightness;
1056 iDisplay.SetBrightness(cds.Brightness);
1059 //Try compute the steps to something that makes sense
1060 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
1061 trackBarBrightness.SmallChange = 1;
1064 buttonFill.Enabled = true;
1065 buttonClear.Enabled = true;
1066 buttonOpen.Enabled = false;
1067 buttonClose.Enabled = true;
1068 trackBarBrightness.Enabled = true;
1069 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1070 //+ " - " + iDisplay.SerialNumber();
1072 if (iDisplay.SupportPowerOnOff())
1074 buttonPowerOn.Enabled = true;
1075 buttonPowerOff.Enabled = true;
1079 buttonPowerOn.Enabled = false;
1080 buttonPowerOff.Enabled = false;
1083 if (iDisplay.SupportClock())
1085 buttonShowClock.Enabled = true;
1086 buttonHideClock.Enabled = true;
1090 buttonShowClock.Enabled = false;
1091 buttonHideClock.Enabled = false;
1095 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1096 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel)>0;
1098 if (cds.ShowVolumeLabel)
1100 iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
1104 iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
1109 //Display is connection not available
1110 //Reflect that in our UI
1111 checkBoxShowVolumeLabel.Enabled = false;
1112 tableLayoutPanel.Enabled = false;
1113 panelDisplay.Enabled = false;
1114 buttonFill.Enabled = false;
1115 buttonClear.Enabled = false;
1116 buttonOpen.Enabled = true;
1117 buttonClose.Enabled = false;
1118 trackBarBrightness.Enabled = false;
1119 buttonPowerOn.Enabled = false;
1120 buttonPowerOff.Enabled = false;
1121 buttonShowClock.Enabled = false;
1122 buttonHideClock.Enabled = false;
1123 toolStripStatusLabelConnect.Text = "Disconnected";
1124 toolStripStatusLabelPower.Text = "N/A";
1133 /// <param name="sender"></param>
1134 /// <param name="e"></param>
1135 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1137 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1138 Properties.Settings.Default.Save();
1142 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1144 //Save our show borders setting
1145 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1146 cds.ShowBorders = checkBoxShowBorders.Checked;
1147 Properties.Settings.Default.Save();
1151 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1153 //Save our connect on startup setting
1154 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1155 Properties.Settings.Default.Save();
1158 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1160 //Save our "Minimize to tray" setting
1161 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1162 Properties.Settings.Default.Save();
1166 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1168 //Save our "Start minimized" setting
1169 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1170 Properties.Settings.Default.Save();
1173 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1175 iStartupManager.Startup = checkBoxAutoStart.Checked;
1179 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1181 //Save our reverse screen setting
1182 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1183 Properties.Settings.Default.Save();
1184 SetupPixelDelegates();
1187 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1189 //Save our inverse colors setting
1190 cds.InverseColors = checkBoxInverseColors.Checked;
1191 Properties.Settings.Default.Save();
1192 SetupPixelDelegates();
1195 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1197 //Save our scale to fit setting
1198 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1199 Properties.Settings.Default.Save();
1201 labelMinFontSize.Enabled = cds.ScaleToFit;
1202 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1205 private void MainForm_Resize(object sender, EventArgs e)
1207 if (WindowState == FormWindowState.Minimized)
1210 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1211 iCreateBitmap = true;
1215 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1217 iNetworkManager.Dispose();
1218 CloseDisplayConnection();
1220 e.Cancel = iClosing;
1223 public void StartServer()
1225 iServiceHost = new ServiceHost
1228 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1231 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1232 iServiceHost.Open();
1235 public void StopServer()
1237 if (iClients.Count > 0 && !iClosing)
1241 BroadcastCloseEvent();
1245 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1247 iClosing = false; //We make sure we force close if asked twice
1252 //We removed that as it often lags for some reason
1253 //iServiceHost.Close();
1257 public void BroadcastCloseEvent()
1259 Trace.TraceInformation("BroadcastCloseEvent - start");
1261 var inactiveClients = new List<string>();
1262 foreach (var client in iClients)
1264 //if (client.Key != eventData.ClientName)
1268 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1269 client.Value.Callback.OnCloseOrder(/*eventData*/);
1271 catch (Exception ex)
1273 inactiveClients.Add(client.Key);
1278 if (inactiveClients.Count > 0)
1280 foreach (var client in inactiveClients)
1282 iClients.Remove(client);
1283 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1287 if (iClients.Count==0)
1294 /// Just remove all our fields.
1296 private void ClearLayout()
1298 tableLayoutPanel.Controls.Clear();
1299 tableLayoutPanel.RowStyles.Clear();
1300 tableLayoutPanel.ColumnStyles.Clear();
1301 iCurrentClientData = null;
1305 /// Just launch a demo client.
1307 private void StartNewClient(string aTopText = "", string aBottomText = "")
1309 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1310 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1311 clientThread.Start(myParams);
1315 private void buttonStartClient_Click(object sender, EventArgs e)
1320 private void buttonSuspend_Click(object sender, EventArgs e)
1322 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1323 timer.Enabled = !timer.Enabled;
1326 buttonSuspend.Text = "Run";
1330 buttonSuspend.Text = "Pause";
1334 private void buttonCloseClients_Click(object sender, EventArgs e)
1336 BroadcastCloseEvent();
1339 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1341 //Root node must have at least one child
1342 if (e.Node.Nodes.Count == 0)
1347 //If the selected node is the root node of a client then switch to it
1348 string sessionId=e.Node.Nodes[0].Text; //First child of a root node is the sessionId
1349 if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at
1351 //We have a valid session just switch to that client
1352 SetCurrentClient(sessionId,true);
1361 /// <param name="aSessionId"></param>
1362 /// <param name="aCallback"></param>
1363 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1365 if (this.InvokeRequired)
1367 //Not in the proper thread, invoke ourselves
1368 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1369 this.Invoke(d, new object[] { aSessionId, aCallback });
1373 //We are in the proper thread
1374 //Add this session to our collection of clients
1375 ClientData newClient = new ClientData(aSessionId, aCallback);
1376 Program.iMainForm.iClients.Add(aSessionId, newClient);
1377 //Add this session to our UI
1378 UpdateClientTreeViewNode(newClient);
1385 /// <param name="aSessionId"></param>
1386 public void RemoveClientThreadSafe(string aSessionId)
1388 if (this.InvokeRequired)
1390 //Not in the proper thread, invoke ourselves
1391 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1392 this.Invoke(d, new object[] { aSessionId });
1396 //We are in the proper thread
1397 //Remove this session from both client collection and UI tree view
1398 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1400 Program.iMainForm.iClients.Remove(aSessionId);
1401 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1404 if (iClients.Count == 0)
1406 //Clear our screen when last client disconnects
1411 //We were closing our form
1412 //All clients are now closed
1413 //Just resume our close operation
1424 /// <param name="aSessionId"></param>
1425 /// <param name="aLayout"></param>
1426 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1428 if (this.InvokeRequired)
1430 //Not in the proper thread, invoke ourselves
1431 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1432 this.Invoke(d, new object[] { aSessionId, aLayout });
1436 ClientData client = iClients[aSessionId];
1439 //Set our client layout then
1440 client.Layout = aLayout;
1442 UpdateClientTreeViewNode(client);
1450 /// <param name="aSessionId"></param>
1451 /// <param name="aField"></param>
1452 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1454 if (this.InvokeRequired)
1456 //Not in the proper thread, invoke ourselves
1457 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1458 this.Invoke(d, new object[] { aSessionId, aField });
1462 //We are in the proper thread
1463 //Call the non-thread-safe variant
1464 SetClientField(aSessionId, aField);
1471 /// <param name="aSessionId"></param>
1472 /// <param name="aField"></param>
1473 private void SetClientField(string aSessionId, DataField aField)
1475 //TODO: should check if the field actually changed?
1477 ClientData client = iClients[aSessionId];
1478 bool layoutChanged = false;
1480 //Make sure all our fields are in place
1481 while (client.Fields.Count < (aField.Index + 1))
1483 //Add a text field with proper index
1484 client.Fields.Add(new DataField(client.Fields.Count));
1485 layoutChanged = true;
1488 if (client.Fields[aField.Index].IsSameLayout(aField))
1490 //Same layout just update our field
1491 client.Fields[aField.Index] = aField;
1492 //If we are updating a field in our current client we need to update it in our panel
1493 if (aSessionId == iCurrentClientSessionId)
1495 if (aField.IsText && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1497 //Text field control already in place, just change the text
1498 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1499 layoutChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1500 label.Text = aField.Text;
1501 label.TextAlign = aField.Alignment;
1503 else if (aField.IsBitmap && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1505 layoutChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1506 //Bitmap field control already in place just change the bitmap
1507 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1508 pictureBox.Image = aField.Bitmap;
1512 layoutChanged = true;
1518 layoutChanged = true;
1519 //Different layout, need to rebuild it
1520 client.Fields[aField.Index] = aField;
1526 UpdateClientTreeViewNode(client);
1527 //Our layout has changed, if we are already the current client we need to update our panel
1528 if (aSessionId == iCurrentClientSessionId)
1530 //Apply layout and set data fields.
1531 UpdateTableLayoutPanel(iCurrentClientData);
1535 //When a client field is set we try switching to this client to present the new information to our user
1536 SetCurrentClient(aSessionId);
1542 /// <param name="aTexts"></param>
1543 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1545 if (this.InvokeRequired)
1547 //Not in the proper thread, invoke ourselves
1548 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1549 this.Invoke(d, new object[] { aSessionId, aFields });
1553 //Put each our text fields in a label control
1554 foreach (DataField field in aFields)
1556 SetClientField(aSessionId, field);
1564 /// <param name="aSessionId"></param>
1565 /// <param name="aName"></param>
1566 public void SetClientNameThreadSafe(string aSessionId, string aName)
1568 if (this.InvokeRequired)
1570 //Not in the proper thread, invoke ourselves
1571 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1572 this.Invoke(d, new object[] { aSessionId, aName });
1576 //We are in the proper thread
1578 ClientData client = iClients[aSessionId];
1582 client.Name = aName;
1583 //Update our tree-view
1584 UpdateClientTreeViewNode(client);
1592 /// <param name="aClient"></param>
1593 private void UpdateClientTreeViewNode(ClientData aClient)
1595 if (aClient == null)
1600 TreeNode node = null;
1601 //Check that our client node already exists
1602 //Get our client root node using its key which is our session ID
1603 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1604 if (nodes.Count()>0)
1606 //We already have a node for that client
1608 //Clear children as we are going to recreate them below
1613 //Client node does not exists create a new one
1614 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1615 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1621 if (aClient.Name != "")
1623 //We have a name, us it as text for our root node
1624 node.Text = aClient.Name;
1625 //Add a child with SessionId
1626 node.Nodes.Add(new TreeNode(aClient.SessionId));
1630 //No name, use session ID instead
1631 node.Text = aClient.SessionId;
1634 if (aClient.Fields.Count > 0)
1636 //Create root node for our texts
1637 TreeNode textsRoot = new TreeNode("Fields");
1638 node.Nodes.Add(textsRoot);
1639 //For each text add a new entry
1640 foreach (DataField field in aClient.Fields)
1642 if (!field.IsBitmap)
1644 DataField textField = (DataField)field;
1645 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1649 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1659 /// Update our table layout row styles to make sure each rows have similar height
1661 private void UpdateTableLayoutRowStyles()
1663 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1665 rowStyle.SizeType = SizeType.Percent;
1666 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1671 /// Update our display table layout.
1673 /// <param name="aLayout"></param>
1674 private void UpdateTableLayoutPanel(ClientData aClient)
1676 if (aClient == null)
1683 TableLayout layout = aClient.Layout;
1686 //First clean our current panel
1687 tableLayoutPanel.Controls.Clear();
1688 tableLayoutPanel.RowStyles.Clear();
1689 tableLayoutPanel.ColumnStyles.Clear();
1690 tableLayoutPanel.RowCount = 0;
1691 tableLayoutPanel.ColumnCount = 0;
1693 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1695 tableLayoutPanel.RowCount++;
1698 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1700 tableLayoutPanel.ColumnCount++;
1703 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1705 //Create our column styles
1706 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1708 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1712 //Create our row styles
1713 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1716 //Check if we already have a control
1717 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1718 if (existingControl!=null)
1720 //We already have a control in that cell as a results of row/col spanning
1721 //Move on to next cell then
1727 //Check if a client field already exists for that cell
1728 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1730 //No client field specified, create a text field by default
1731 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1734 //Create a control corresponding to the field specified for that cell
1735 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1736 Control control = CreateControlForDataField(field);
1738 //Add newly created control to our table layout at the specified row and column
1739 tableLayoutPanel.Controls.Add(control, i, j);
1740 //Make sure we specify row and column span for that new control
1741 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1742 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1747 while (aClient.Fields.Count > fieldCount)
1749 //We have too much fields for this layout
1750 //Just discard them until we get there
1751 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1758 /// Check our type of data field and create corresponding control
1760 /// <param name="aField"></param>
1761 private Control CreateControlForDataField(DataField aField)
1763 Control control=null;
1764 if (!aField.IsBitmap)
1766 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1767 label.AutoEllipsis = true;
1768 label.AutoSize = true;
1769 label.BackColor = System.Drawing.Color.Transparent;
1770 label.Dock = System.Windows.Forms.DockStyle.Fill;
1771 label.Location = new System.Drawing.Point(1, 1);
1772 label.Margin = new System.Windows.Forms.Padding(0);
1773 label.Name = "marqueeLabel" + aField.Index;
1774 label.OwnTimer = false;
1775 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1776 label.Separator = cds.Separator;
1777 label.MinFontSize = cds.MinFontSize;
1778 label.ScaleToFit = cds.ScaleToFit;
1779 //control.Size = new System.Drawing.Size(254, 30);
1780 //control.TabIndex = 2;
1781 label.Font = cds.Font;
1783 label.TextAlign = aField.Alignment;
1784 label.UseCompatibleTextRendering = true;
1785 label.Text = aField.Text;
1791 //Create picture box
1792 PictureBox picture = new PictureBox();
1793 picture.AutoSize = true;
1794 picture.BackColor = System.Drawing.Color.Transparent;
1795 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1796 picture.Location = new System.Drawing.Point(1, 1);
1797 picture.Margin = new System.Windows.Forms.Padding(0);
1798 picture.Name = "pictureBox" + aField;
1800 picture.Image = aField.Bitmap;
1809 /// Called when the user selected a new display type.
1811 /// <param name="sender"></param>
1812 /// <param name="e"></param>
1813 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1815 //Store the selected display type in our settings
1816 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1817 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1818 Properties.Settings.Default.Save();
1820 //Try re-opening the display connection if we were already connected.
1821 //Otherwise just update our status to reflect display type change.
1822 if (iDisplay.IsOpen())
1824 OpenDisplayConnection();
1832 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1834 if (maskedTextBoxTimerInterval.Text != "")
1836 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1840 timer.Interval = interval;
1841 cds.TimerInterval = timer.Interval;
1842 Properties.Settings.Default.Save();
1847 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1849 if (maskedTextBoxMinFontSize.Text != "")
1851 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1853 if (minFontSize > 0)
1855 cds.MinFontSize = minFontSize;
1856 Properties.Settings.Default.Save();
1857 //We need to recreate our layout for that change to take effect
1858 UpdateTableLayoutPanel(iCurrentClientData);
1864 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1866 if (maskedTextBoxScrollingSpeed.Text != "")
1868 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1870 if (scrollingSpeed > 0)
1872 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1873 Properties.Settings.Default.Save();
1874 //We need to recreate our layout for that change to take effect
1875 UpdateTableLayoutPanel(iCurrentClientData);
1880 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1882 cds.Separator = textBoxScrollLoopSeparator.Text;
1883 Properties.Settings.Default.Save();
1885 //Update our text fields
1886 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1888 ctrl.Separator = cds.Separator;
1893 private void buttonPowerOn_Click(object sender, EventArgs e)
1898 private void buttonPowerOff_Click(object sender, EventArgs e)
1900 iDisplay.PowerOff();
1903 private void buttonShowClock_Click(object sender, EventArgs e)
1908 private void buttonHideClock_Click(object sender, EventArgs e)
1913 private void buttonUpdate_Click(object sender, EventArgs e)
1915 InstallUpdateSyncWithInfo();
1923 if (!iDisplay.IsOpen())
1928 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1929 iSkipFrameRendering = true;
1932 iDisplay.SwapBuffers();
1933 //Then show our clock
1934 iDisplay.ShowClock();
1942 if (!iDisplay.IsOpen())
1947 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1948 iSkipFrameRendering = false;
1949 iDisplay.HideClock();
1952 private void InstallUpdateSyncWithInfo()
1954 UpdateCheckInfo info = null;
1956 if (ApplicationDeployment.IsNetworkDeployed)
1958 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1962 info = ad.CheckForDetailedUpdate();
1965 catch (DeploymentDownloadException dde)
1967 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);
1970 catch (InvalidDeploymentException ide)
1972 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);
1975 catch (InvalidOperationException ioe)
1977 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1981 if (info.UpdateAvailable)
1983 Boolean doUpdate = true;
1985 if (!info.IsUpdateRequired)
1987 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1988 if (!(DialogResult.OK == dr))
1995 // Display a message that the app MUST reboot. Display the minimum required version.
1996 MessageBox.Show("This application has detected a mandatory update from your current " +
1997 "version to version " + info.MinimumRequiredVersion.ToString() +
1998 ". The application will now install the update and restart.",
1999 "Update Available", MessageBoxButtons.OK,
2000 MessageBoxIcon.Information);
2008 MessageBox.Show("The application has been upgraded, and will now restart.");
2009 Application.Restart();
2011 catch (DeploymentDownloadException dde)
2013 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
2020 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
2029 private void SysTrayHideShow()
2035 WindowState = FormWindowState.Normal;
2040 /// Use to handle minimize events.
2042 /// <param name="sender"></param>
2043 /// <param name="e"></param>
2044 private void MainForm_SizeChanged(object sender, EventArgs e)
2046 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2058 /// <param name="sender"></param>
2059 /// <param name="e"></param>
2060 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2062 //Our table layout size has changed which means our display size has changed.
2063 //We need to re-create our bitmap.
2064 iCreateBitmap = true;
2070 /// <param name="sender"></param>
2071 /// <param name="e"></param>
2072 private void buttonSelectFile_Click(object sender, EventArgs e)
2074 //openFileDialog1.InitialDirectory = "c:\\";
2075 //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
2076 //openFileDialog.FilterIndex = 1;
2077 openFileDialog.RestoreDirectory = true;
2079 if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
2081 labelStartFileName.Text = openFileDialog.FileName;
2082 Properties.Settings.Default.StartFileName = openFileDialog.FileName;
2083 Properties.Settings.Default.Save();