Updating SharpLibHid.
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 //Don't change a thing if the layout is the same
1440 if (!client.Layout.IsSameAs(aLayout))
1442 //Set our client layout then
1443 client.Layout = aLayout;
1445 UpdateClientTreeViewNode(client);
1454 /// <param name="aSessionId"></param>
1455 /// <param name="aField"></param>
1456 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1458 if (this.InvokeRequired)
1460 //Not in the proper thread, invoke ourselves
1461 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1462 this.Invoke(d, new object[] { aSessionId, aField });
1466 //We are in the proper thread
1467 //Call the non-thread-safe variant
1468 SetClientField(aSessionId, aField);
1475 /// <param name="aSessionId"></param>
1476 /// <param name="aField"></param>
1477 private void SetClientField(string aSessionId, DataField aField)
1479 //TODO: should check if the field actually changed?
1481 ClientData client = iClients[aSessionId];
1482 bool layoutChanged = false;
1483 bool contentChanged = true;
1485 //Make sure all our fields are in place
1486 while (client.Fields.Count < (aField.Index + 1))
1488 //Add a data field with proper index
1489 client.Fields.Add(new DataField(client.Fields.Count));
1490 layoutChanged = true;
1493 //Now that we know hour fields are in place just update that one
1494 client.Fields[aField.Index] = aField;
1496 if (client.Fields[aField.Index].IsSameLayout(aField))
1498 //If we are updating a field in our current client we need to update it in our panel
1499 if (aSessionId == iCurrentClientSessionId)
1501 if (aField.IsText && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1503 //Text field control already in place, just change the text
1504 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1505 contentChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1506 label.Text = aField.Text;
1507 label.TextAlign = aField.Alignment;
1509 else if (aField.IsBitmap && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1511 contentChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1512 //Bitmap field control already in place just change the bitmap
1513 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1514 pictureBox.Image = aField.Bitmap;
1518 layoutChanged = true;
1524 layoutChanged = true;
1527 //If either content or layout changed we need to update our tree view to reflect the changes
1528 if (contentChanged || layoutChanged)
1530 UpdateClientTreeViewNode(client);
1534 Debug.Print("Layout changed");
1535 //Our layout has changed, if we are already the current client we need to update our panel
1536 if (aSessionId == iCurrentClientSessionId)
1538 //Apply layout and set data fields.
1539 UpdateTableLayoutPanel(iCurrentClientData);
1544 //When a client field is set we try switching to this client to present the new information to our user
1545 SetCurrentClient(aSessionId);
1551 /// <param name="aTexts"></param>
1552 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1554 if (this.InvokeRequired)
1556 //Not in the proper thread, invoke ourselves
1557 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1558 this.Invoke(d, new object[] { aSessionId, aFields });
1562 //Put each our text fields in a label control
1563 foreach (DataField field in aFields)
1565 SetClientField(aSessionId, field);
1573 /// <param name="aSessionId"></param>
1574 /// <param name="aName"></param>
1575 public void SetClientNameThreadSafe(string aSessionId, string aName)
1577 if (this.InvokeRequired)
1579 //Not in the proper thread, invoke ourselves
1580 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1581 this.Invoke(d, new object[] { aSessionId, aName });
1585 //We are in the proper thread
1587 ClientData client = iClients[aSessionId];
1591 client.Name = aName;
1592 //Update our tree-view
1593 UpdateClientTreeViewNode(client);
1601 /// <param name="aClient"></param>
1602 private void UpdateClientTreeViewNode(ClientData aClient)
1604 Debug.Print("UpdateClientTreeViewNode");
1606 if (aClient == null)
1611 TreeNode node = null;
1612 //Check that our client node already exists
1613 //Get our client root node using its key which is our session ID
1614 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1615 if (nodes.Count()>0)
1617 //We already have a node for that client
1619 //Clear children as we are going to recreate them below
1624 //Client node does not exists create a new one
1625 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1626 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1632 if (aClient.Name != "")
1634 //We have a name, us it as text for our root node
1635 node.Text = aClient.Name;
1636 //Add a child with SessionId
1637 node.Nodes.Add(new TreeNode(aClient.SessionId));
1641 //No name, use session ID instead
1642 node.Text = aClient.SessionId;
1645 if (aClient.Fields.Count > 0)
1647 //Create root node for our texts
1648 TreeNode textsRoot = new TreeNode("Fields");
1649 node.Nodes.Add(textsRoot);
1650 //For each text add a new entry
1651 foreach (DataField field in aClient.Fields)
1653 if (!field.IsBitmap)
1655 DataField textField = (DataField)field;
1656 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1660 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1670 /// Update our table layout row styles to make sure each rows have similar height
1672 private void UpdateTableLayoutRowStyles()
1674 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1676 rowStyle.SizeType = SizeType.Percent;
1677 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1682 /// Update our display table layout.
1684 /// <param name="aLayout"></param>
1685 private void UpdateTableLayoutPanel(ClientData aClient)
1687 Debug.Print("UpdateClientTreeViewNode");
1689 if (aClient == null)
1696 TableLayout layout = aClient.Layout;
1699 //First clean our current panel
1700 tableLayoutPanel.Controls.Clear();
1701 tableLayoutPanel.RowStyles.Clear();
1702 tableLayoutPanel.ColumnStyles.Clear();
1703 tableLayoutPanel.RowCount = 0;
1704 tableLayoutPanel.ColumnCount = 0;
1706 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1708 tableLayoutPanel.RowCount++;
1711 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1713 tableLayoutPanel.ColumnCount++;
1716 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1718 //Create our column styles
1719 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1721 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1725 //Create our row styles
1726 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1729 //Check if we already have a control
1730 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1731 if (existingControl!=null)
1733 //We already have a control in that cell as a results of row/col spanning
1734 //Move on to next cell then
1740 //Check if a client field already exists for that cell
1741 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1743 //No client field specified, create a text field by default
1744 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1747 //Create a control corresponding to the field specified for that cell
1748 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1749 Control control = CreateControlForDataField(field);
1751 //Add newly created control to our table layout at the specified row and column
1752 tableLayoutPanel.Controls.Add(control, i, j);
1753 //Make sure we specify row and column span for that new control
1754 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1755 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1760 while (aClient.Fields.Count > fieldCount)
1762 //We have too much fields for this layout
1763 //Just discard them until we get there
1764 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1771 /// Check our type of data field and create corresponding control
1773 /// <param name="aField"></param>
1774 private Control CreateControlForDataField(DataField aField)
1776 Control control=null;
1777 if (!aField.IsBitmap)
1779 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1780 label.AutoEllipsis = true;
1781 label.AutoSize = true;
1782 label.BackColor = System.Drawing.Color.Transparent;
1783 label.Dock = System.Windows.Forms.DockStyle.Fill;
1784 label.Location = new System.Drawing.Point(1, 1);
1785 label.Margin = new System.Windows.Forms.Padding(0);
1786 label.Name = "marqueeLabel" + aField.Index;
1787 label.OwnTimer = false;
1788 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1789 label.Separator = cds.Separator;
1790 label.MinFontSize = cds.MinFontSize;
1791 label.ScaleToFit = cds.ScaleToFit;
1792 //control.Size = new System.Drawing.Size(254, 30);
1793 //control.TabIndex = 2;
1794 label.Font = cds.Font;
1796 label.TextAlign = aField.Alignment;
1797 label.UseCompatibleTextRendering = true;
1798 label.Text = aField.Text;
1804 //Create picture box
1805 PictureBox picture = new PictureBox();
1806 picture.AutoSize = true;
1807 picture.BackColor = System.Drawing.Color.Transparent;
1808 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1809 picture.Location = new System.Drawing.Point(1, 1);
1810 picture.Margin = new System.Windows.Forms.Padding(0);
1811 picture.Name = "pictureBox" + aField;
1813 picture.Image = aField.Bitmap;
1822 /// Called when the user selected a new display type.
1824 /// <param name="sender"></param>
1825 /// <param name="e"></param>
1826 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1828 //Store the selected display type in our settings
1829 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1830 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1831 Properties.Settings.Default.Save();
1833 //Try re-opening the display connection if we were already connected.
1834 //Otherwise just update our status to reflect display type change.
1835 if (iDisplay.IsOpen())
1837 OpenDisplayConnection();
1845 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1847 if (maskedTextBoxTimerInterval.Text != "")
1849 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1853 timer.Interval = interval;
1854 cds.TimerInterval = timer.Interval;
1855 Properties.Settings.Default.Save();
1860 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1862 if (maskedTextBoxMinFontSize.Text != "")
1864 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1866 if (minFontSize > 0)
1868 cds.MinFontSize = minFontSize;
1869 Properties.Settings.Default.Save();
1870 //We need to recreate our layout for that change to take effect
1871 UpdateTableLayoutPanel(iCurrentClientData);
1877 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1879 if (maskedTextBoxScrollingSpeed.Text != "")
1881 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1883 if (scrollingSpeed > 0)
1885 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1886 Properties.Settings.Default.Save();
1887 //We need to recreate our layout for that change to take effect
1888 UpdateTableLayoutPanel(iCurrentClientData);
1893 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1895 cds.Separator = textBoxScrollLoopSeparator.Text;
1896 Properties.Settings.Default.Save();
1898 //Update our text fields
1899 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1901 ctrl.Separator = cds.Separator;
1906 private void buttonPowerOn_Click(object sender, EventArgs e)
1911 private void buttonPowerOff_Click(object sender, EventArgs e)
1913 iDisplay.PowerOff();
1916 private void buttonShowClock_Click(object sender, EventArgs e)
1921 private void buttonHideClock_Click(object sender, EventArgs e)
1926 private void buttonUpdate_Click(object sender, EventArgs e)
1928 InstallUpdateSyncWithInfo();
1936 if (!iDisplay.IsOpen())
1941 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1942 iSkipFrameRendering = true;
1945 iDisplay.SwapBuffers();
1946 //Then show our clock
1947 iDisplay.ShowClock();
1955 if (!iDisplay.IsOpen())
1960 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1961 iSkipFrameRendering = false;
1962 iDisplay.HideClock();
1965 private void InstallUpdateSyncWithInfo()
1967 UpdateCheckInfo info = null;
1969 if (ApplicationDeployment.IsNetworkDeployed)
1971 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1975 info = ad.CheckForDetailedUpdate();
1978 catch (DeploymentDownloadException dde)
1980 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);
1983 catch (InvalidDeploymentException ide)
1985 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);
1988 catch (InvalidOperationException ioe)
1990 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1994 if (info.UpdateAvailable)
1996 Boolean doUpdate = true;
1998 if (!info.IsUpdateRequired)
2000 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
2001 if (!(DialogResult.OK == dr))
2008 // Display a message that the app MUST reboot. Display the minimum required version.
2009 MessageBox.Show("This application has detected a mandatory update from your current " +
2010 "version to version " + info.MinimumRequiredVersion.ToString() +
2011 ". The application will now install the update and restart.",
2012 "Update Available", MessageBoxButtons.OK,
2013 MessageBoxIcon.Information);
2021 MessageBox.Show("The application has been upgraded, and will now restart.");
2022 Application.Restart();
2024 catch (DeploymentDownloadException dde)
2026 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
2033 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
2042 private void SysTrayHideShow()
2048 WindowState = FormWindowState.Normal;
2053 /// Use to handle minimize events.
2055 /// <param name="sender"></param>
2056 /// <param name="e"></param>
2057 private void MainForm_SizeChanged(object sender, EventArgs e)
2059 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2071 /// <param name="sender"></param>
2072 /// <param name="e"></param>
2073 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2075 //Our table layout size has changed which means our display size has changed.
2076 //We need to re-create our bitmap.
2077 iCreateBitmap = true;
2083 /// <param name="sender"></param>
2084 /// <param name="e"></param>
2085 private void buttonSelectFile_Click(object sender, EventArgs e)
2087 //openFileDialog1.InitialDirectory = "c:\\";
2088 //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
2089 //openFileDialog.FilterIndex = 1;
2090 openFileDialog.RestoreDirectory = true;
2092 if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
2094 labelStartFileName.Text = openFileDialog.FileName;
2095 Properties.Settings.Default.StartFileName = openFileDialog.FileName;
2096 Properties.Settings.Default.Save();