Looks like we have a solution for our bring to foreground problem.
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;
48 namespace SharpDisplayManager
51 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
52 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
53 //Delegates are used for our thread safe method
54 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
55 public delegate void RemoveClientDelegate(string aSessionId);
56 public delegate void SetFieldDelegate(string SessionId, DataField aField);
57 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
58 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
59 public delegate void SetClientNameDelegate(string aSessionId, string aName);
60 public delegate void PlainUpdateDelegate();
64 /// Our Display manager main form
66 [System.ComponentModel.DesignerCategory("Form")]
67 public partial class MainForm : MainFormHid, IMMNotificationClient
69 DateTime LastTickTime;
71 System.Drawing.Bitmap iBmp;
72 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
73 ServiceHost iServiceHost;
74 // Our collection of clients sorted by session id.
75 public Dictionary<string, ClientData> iClients;
76 // The name of the client which informations are currently displayed.
77 public string iCurrentClientSessionId;
78 ClientData iCurrentClientData;
82 public bool iSkipFrameRendering;
83 //Function pointer for pixel color filtering
84 ColorProcessingDelegate iColorFx;
85 //Function pointer for pixel X coordinate intercept
86 CoordinateTranslationDelegate iScreenX;
87 //Function pointer for pixel Y coordinate intercept
88 CoordinateTranslationDelegate iScreenY;
90 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
91 private MMDevice iMultiMediaDevice;
93 private NetworkManager iNetworkManager;
97 /// Manage run when Windows startup option
99 private StartupManager iStartupManager;
102 /// System tray icon.
104 private NotifyIconAdv iNotifyIcon;
108 iSkipFrameRendering = false;
110 iCurrentClientSessionId = "";
111 iCurrentClientData = null;
112 LastTickTime = DateTime.Now;
113 //Instantiate our display and register for events notifications
114 iDisplay = new Display();
115 iDisplay.OnOpened += OnDisplayOpened;
116 iDisplay.OnClosed += OnDisplayClosed;
118 iClients = new Dictionary<string, ClientData>();
119 iStartupManager = new StartupManager();
120 iNotifyIcon = new NotifyIconAdv();
122 //Have our designer initialize its controls
123 InitializeComponent();
125 //Populate device types
126 PopulateDeviceTypes();
128 //Initial status update
131 //We have a bug when drawing minimized and reusing our bitmap
132 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
133 iCreateBitmap = false;
135 //Minimize our window if desired
136 if (Properties.Settings.Default.StartMinimized)
138 WindowState = FormWindowState.Minimized;
146 /// <param name="sender"></param>
147 /// <param name="e"></param>
148 private void MainForm_Load(object sender, EventArgs e)
150 //Check if we are running a Click Once deployed application
151 if (ApplicationDeployment.IsNetworkDeployed)
153 //This is a proper Click Once installation, fetch and show our version number
154 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
158 //Not a proper Click Once installation, assuming development build then
159 this.Text += " - development";
163 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
164 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
165 UpdateAudioDeviceAndMasterVolumeThreadSafe();
168 iNetworkManager = new NetworkManager();
169 iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
170 UpdateNetworkStatus();
172 //Setup notification icon
175 // To make sure start up with minimize to tray works
176 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
182 //When not debugging we want the screen to be empty until a client takes over
185 //When developing we want at least one client for testing
186 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
189 //Open display connection on start-up if needed
190 if (Properties.Settings.Default.DisplayConnectOnStartup)
192 OpenDisplayConnection();
195 //Start our server so that we can get client requests
198 //Register for HID events
199 RegisterHidDevices();
203 /// Called when our display is opened.
205 /// <param name="aDisplay"></param>
206 private void OnDisplayOpened(Display aDisplay)
208 //Make sure we resume frame rendering
209 iSkipFrameRendering = false;
211 //Set our screen size now that our display is connected
212 //Our panelDisplay is the container of our tableLayoutPanel
213 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
214 //panelDisplay needs an extra 2 pixels for borders on each sides
215 //tableLayoutPanel will eventually be the exact size of our display
216 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
217 panelDisplay.Size = size;
219 //Our display was just opened, update our UI
221 //Initiate asynchronous request
222 iDisplay.RequestFirmwareRevision();
225 UpdateMasterVolumeThreadSafe();
227 UpdateNetworkStatus();
230 //Testing icon in debug, no arm done if icon not supported
231 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
232 //iDisplay.SetAllIconsStatus(2);
238 /// Called when our display is closed.
240 /// <param name="aDisplay"></param>
241 private void OnDisplayClosed(Display aDisplay)
243 //Our display was just closed, update our UI consequently
247 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
249 //Update network status
250 UpdateNetworkStatus();
254 /// Update our Network Status
256 private void UpdateNetworkStatus()
258 if (iDisplay.IsOpen())
260 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconInternet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
261 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
266 int iLastNetworkIconIndex = 0;
267 int iUpdateCountSinceLastNetworkAnimation = 0;
272 private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
274 iUpdateCountSinceLastNetworkAnimation++;
275 iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
277 if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
279 int iconCount=iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal);
282 //Prevents div by zero and other undefined behavior
285 iLastNetworkIconIndex++;
286 iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2);
287 for (int i=0;i<iconCount;i++)
289 if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && !(i == 1 && iLastNetworkIconIndex > 4))
291 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
295 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
304 /// Receive volume change notification and reflect changes on our slider.
306 /// <param name="data"></param>
307 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
309 UpdateMasterVolumeThreadSafe();
313 /// Update master volume when user moves our slider.
315 /// <param name="sender"></param>
316 /// <param name="e"></param>
317 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
319 //Just like Windows Volume Mixer we unmute if the volume is adjusted
320 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
321 //Set volume level according to our volume slider new position
322 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
327 /// Mute check box changed.
329 /// <param name="sender"></param>
330 /// <param name="e"></param>
331 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
333 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
337 /// Device State Changed
339 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
344 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
349 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
352 /// Default Device Changed
354 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
356 if (role == Role.Multimedia && flow == DataFlow.Render)
358 UpdateAudioDeviceAndMasterVolumeThreadSafe();
363 /// Property Value Changed
365 /// <param name="pwstrDeviceId"></param>
366 /// <param name="key"></param>
367 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
373 /// Update master volume indicators based our current system states.
374 /// This typically includes volume levels and mute status.
376 private void UpdateMasterVolumeThreadSafe()
378 if (this.InvokeRequired)
380 //Not in the proper thread, invoke ourselves
381 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
382 this.Invoke(d, new object[] { });
386 //Update volume slider
387 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
388 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
389 //Update mute checkbox
390 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
392 //If our display connection is open we need to update its icons
393 if (iDisplay.IsOpen())
395 //First take care our our volume level icons
396 int volumeIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume);
397 if (volumeIconCount > 0)
399 //Compute current volume level from system level and the number of segments in our display volume bar.
400 //That tells us how many segments in our volume bar needs to be turned on.
401 float currentVolume = volumeLevelScalar * volumeIconCount;
402 int segmentOnCount = Convert.ToInt32(currentVolume);
403 //Check if our segment count was rounded up, this will later be used for half brightness segment
404 bool roundedUp = segmentOnCount > currentVolume;
406 for (int i = 0; i < volumeIconCount; i++)
408 if (i < segmentOnCount)
410 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
411 if (i == segmentOnCount - 1 && roundedUp)
414 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, (iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1)/2);
419 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1);
424 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 0);
429 //Take care our our mute icon
430 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconMute, iMultiMediaDevice.AudioEndpointVolume.Mute);
438 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
440 if (this.InvokeRequired)
442 //Not in the proper thread, invoke ourselves
443 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
444 this.Invoke(d, new object[] { });
448 //We are in the correct thread just go ahead.
451 //Get our master volume
452 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
454 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
456 //Show our volume in our track bar
457 UpdateMasterVolumeThreadSafe();
459 //Register to get volume modifications
460 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
462 trackBarMasterVolume.Enabled = true;
466 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
467 Debug.WriteLine(ex.ToString());
468 //Something went wrong S/PDIF device ca throw exception I guess
469 trackBarMasterVolume.Enabled = false;
476 private void PopulateDeviceTypes()
478 int count = Display.TypeCount();
480 for (int i = 0; i < count; i++)
482 comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
489 private void SetupTrayIcon()
491 iNotifyIcon.Icon = GetIcon("vfd.ico");
492 iNotifyIcon.Text = "Sharp Display Manager";
493 iNotifyIcon.Visible = true;
495 //Double click toggles visibility - typically brings up the application
496 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
501 //Adding a context menu, useful to be able to exit the application
502 ContextMenu contextMenu = new ContextMenu();
503 //Context menu item to toggle visibility
504 MenuItem hideShowItem = new MenuItem("Hide/Show");
505 hideShowItem.Click += delegate(object obj, EventArgs args)
509 contextMenu.MenuItems.Add(hideShowItem);
511 //Context menu item separator
512 contextMenu.MenuItems.Add(new MenuItem("-"));
514 //Context menu exit item
515 MenuItem exitItem = new MenuItem("Exit");
516 exitItem.Click += delegate(object obj, EventArgs args)
520 contextMenu.MenuItems.Add(exitItem);
522 iNotifyIcon.ContextMenu = contextMenu;
526 /// Access icons from embedded resources.
528 /// <param name="name"></param>
529 /// <returns></returns>
530 public static Icon GetIcon(string name)
532 name = "SharpDisplayManager.Resources." + name;
535 Assembly.GetExecutingAssembly().GetManifestResourceNames();
536 for (int i = 0; i < names.Length; i++)
538 if (names[i].Replace('\\', '.') == name)
540 using (Stream stream = Assembly.GetExecutingAssembly().
541 GetManifestResourceStream(names[i]))
543 return new Icon(stream);
553 /// Set our current client.
554 /// This will take care of applying our client layout and set data fields.
556 /// <param name="aSessionId"></param>
557 void SetCurrentClient(string aSessionId)
559 if (aSessionId == iCurrentClientSessionId)
561 //Given client is already the current one.
562 //Don't bother changing anything then.
566 //Set current client ID.
567 iCurrentClientSessionId = aSessionId;
568 //Fetch and set current client data.
569 iCurrentClientData = iClients[aSessionId];
570 //Apply layout and set data fields.
571 UpdateTableLayoutPanel(iCurrentClientData);
574 private void buttonFont_Click(object sender, EventArgs e)
576 //fontDialog.ShowColor = true;
577 //fontDialog.ShowApply = true;
578 fontDialog.ShowEffects = true;
579 fontDialog.Font = cds.Font;
581 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
583 //fontDialog.ShowHelp = true;
585 //fontDlg.MaxSize = 40;
586 //fontDlg.MinSize = 22;
588 //fontDialog.Parent = this;
589 //fontDialog.StartPosition = FormStartPosition.CenterParent;
591 //DlgBox.ShowDialog(fontDialog);
593 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
594 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
596 //Set the fonts to all our labels in our layout
597 foreach (Control ctrl in tableLayoutPanel.Controls)
599 if (ctrl is MarqueeLabel)
601 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
606 cds.Font = fontDialog.Font;
607 Properties.Settings.Default.Save();
616 void CheckFontHeight()
618 //Show font height and width
619 labelFontHeight.Text = "Font height: " + cds.Font.Height;
620 float charWidth = IsFixedWidth(cds.Font);
621 if (charWidth == 0.0f)
623 labelFontWidth.Visible = false;
627 labelFontWidth.Visible = true;
628 labelFontWidth.Text = "Font width: " + charWidth;
631 MarqueeLabel label = null;
632 //Get the first label control we can find
633 foreach (Control ctrl in tableLayoutPanel.Controls)
635 if (ctrl is MarqueeLabel)
637 label = (MarqueeLabel)ctrl;
642 //Now check font height and show a warning if needed.
643 if (label != null && label.Font.Height > label.Height)
645 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
646 labelWarning.Visible = true;
650 labelWarning.Visible = false;
655 private void buttonCapture_Click(object sender, EventArgs e)
657 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
658 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
659 //Bitmap bmpToSave = new Bitmap(bmp);
660 bmp.Save("D:\\capture.png");
662 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
665 string outputFileName = "d:\\capture.png";
666 using (MemoryStream memory = new MemoryStream())
668 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
670 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
671 byte[] bytes = memory.ToArray();
672 fs.Write(bytes, 0, bytes.Length);
679 private void CheckForRequestResults()
681 if (iDisplay.IsRequestPending())
683 switch (iDisplay.AttemptRequestCompletion())
685 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
686 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
687 //Issue next request then
688 iDisplay.RequestPowerSupplyStatus();
691 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
692 if (iDisplay.PowerSupplyStatus())
694 toolStripStatusLabelPower.Text = "ON";
698 toolStripStatusLabelPower.Text = "OFF";
700 //Issue next request then
701 iDisplay.RequestDeviceId();
704 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
705 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
706 //No more request to issue
712 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
714 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
721 public static uint ColorUntouched(int aX, int aY, uint aPixel)
726 public static uint ColorInversed(int aX, int aY, uint aPixel)
731 public static uint ColorChessboard(int aX, int aY, uint aPixel)
733 if ((aX % 2 == 0) && (aY % 2 == 0))
737 else if ((aX % 2 != 0) && (aY % 2 != 0))
745 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
747 return aBmp.Width - aX - 1;
750 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
752 return iBmp.Height - aY - 1;
755 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
760 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
766 /// Select proper pixel delegates according to our current settings.
768 private void SetupPixelDelegates()
770 //Select our pixel processing routine
771 if (cds.InverseColors)
773 //iColorFx = ColorChessboard;
774 iColorFx = ColorInversed;
778 iColorFx = ColorWhiteIsOn;
781 //Select proper coordinate translation functions
782 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
783 if (cds.ReverseScreen)
785 iScreenX = ScreenReversedX;
786 iScreenY = ScreenReversedY;
796 //This is our timer tick responsible to perform our render
797 private void timer_Tick(object sender, EventArgs e)
799 //Update our animations
800 DateTime NewTickTime = DateTime.Now;
802 UpdateNetworkSignal(LastTickTime, NewTickTime);
804 //Update animation for all our marquees
805 foreach (Control ctrl in tableLayoutPanel.Controls)
807 if (ctrl is MarqueeLabel)
809 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
814 if (iDisplay.IsOpen())
816 CheckForRequestResults();
818 //Check if frame rendering is needed
819 //Typically used when showing clock
820 if (!iSkipFrameRendering)
825 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
827 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
828 //iBmp.Save("D:\\capture.png");
830 //Send it to our display
831 for (int i = 0; i < iBmp.Width; i++)
833 for (int j = 0; j < iBmp.Height; j++)
837 //Get our processed pixel coordinates
838 int x = iScreenX(iBmp, i);
839 int y = iScreenY(iBmp, j);
841 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
842 //Apply color effects
843 color = iColorFx(x, y, color);
845 iDisplay.SetPixel(x, y, color);
850 iDisplay.SwapBuffers();
854 //Compute instant FPS
855 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
857 LastTickTime = NewTickTime;
862 /// Attempt to establish connection with our display hardware.
864 private void OpenDisplayConnection()
866 CloseDisplayConnection();
868 if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
871 toolStripStatusLabelConnect.Text = "Connection error";
875 private void CloseDisplayConnection()
877 //Status will be updated upon receiving the closed event
879 if (iDisplay == null || !iDisplay.IsOpen())
884 //Do not clear if we gave up on rendering already.
885 //This means we will keep on displaying clock on MDM166AA for instance.
886 if (!iSkipFrameRendering)
889 iDisplay.SwapBuffers();
892 iDisplay.SetAllIconsStatus(0); //Turn off all icons
896 private void buttonOpen_Click(object sender, EventArgs e)
898 OpenDisplayConnection();
901 private void buttonClose_Click(object sender, EventArgs e)
903 CloseDisplayConnection();
906 private void buttonClear_Click(object sender, EventArgs e)
909 iDisplay.SwapBuffers();
912 private void buttonFill_Click(object sender, EventArgs e)
915 iDisplay.SwapBuffers();
918 private void trackBarBrightness_Scroll(object sender, EventArgs e)
920 cds.Brightness = trackBarBrightness.Value;
921 Properties.Settings.Default.Save();
922 iDisplay.SetBrightness(trackBarBrightness.Value);
928 /// CDS stands for Current Display Settings
930 private DisplaySettings cds
934 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
935 if (settings == null)
937 settings = new DisplaysSettings();
939 Properties.Settings.Default.DisplaysSettings = settings;
942 //Make sure all our settings have been created
943 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
945 settings.Displays.Add(new DisplaySettings());
948 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
949 return displaySettings;
954 /// Check if the given font has a fixed character pitch.
956 /// <param name="ft"></param>
957 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
958 public float IsFixedWidth(Font ft)
960 Graphics g = CreateGraphics();
961 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
962 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
964 bool fixedWidth = true;
966 foreach (char c in charSizes)
967 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
979 /// Synchronize UI with settings
981 private void UpdateStatus()
984 checkBoxShowBorders.Checked = cds.ShowBorders;
985 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
987 //Set the proper font to each of our labels
988 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
990 ctrl.Font = cds.Font;
994 //Check if "run on Windows startup" is enabled
995 checkBoxAutoStart.Checked = iStartupManager.Startup;
997 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
998 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
999 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
1000 labelStartFileName.Text = Properties.Settings.Default.StartFileName;
1001 checkBoxReverseScreen.Checked = cds.ReverseScreen;
1002 checkBoxInverseColors.Checked = cds.InverseColors;
1003 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
1004 checkBoxScaleToFit.Checked = cds.ScaleToFit;
1005 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1006 labelMinFontSize.Enabled = cds.ScaleToFit;
1007 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
1008 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
1009 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
1010 timer.Interval = cds.TimerInterval;
1011 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
1012 textBoxScrollLoopSeparator.Text = cds.Separator;
1014 SetupPixelDelegates();
1016 if (iDisplay.IsOpen())
1018 //We have a display connection
1019 //Reflect that in our UI
1021 tableLayoutPanel.Enabled = true;
1022 panelDisplay.Enabled = true;
1024 //Only setup brightness if display is open
1025 trackBarBrightness.Minimum = iDisplay.MinBrightness();
1026 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
1027 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
1029 //Brightness out of range, this can occur when using auto-detect
1030 //Use max brightness instead
1031 trackBarBrightness.Value = iDisplay.MaxBrightness();
1032 iDisplay.SetBrightness(iDisplay.MaxBrightness());
1036 trackBarBrightness.Value = cds.Brightness;
1037 iDisplay.SetBrightness(cds.Brightness);
1040 //Try compute the steps to something that makes sense
1041 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
1042 trackBarBrightness.SmallChange = 1;
1045 buttonFill.Enabled = true;
1046 buttonClear.Enabled = true;
1047 buttonOpen.Enabled = false;
1048 buttonClose.Enabled = true;
1049 trackBarBrightness.Enabled = true;
1050 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1051 //+ " - " + iDisplay.SerialNumber();
1053 if (iDisplay.SupportPowerOnOff())
1055 buttonPowerOn.Enabled = true;
1056 buttonPowerOff.Enabled = true;
1060 buttonPowerOn.Enabled = false;
1061 buttonPowerOff.Enabled = false;
1064 if (iDisplay.SupportClock())
1066 buttonShowClock.Enabled = true;
1067 buttonHideClock.Enabled = true;
1071 buttonShowClock.Enabled = false;
1072 buttonHideClock.Enabled = false;
1076 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1077 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel)>0;
1079 if (cds.ShowVolumeLabel)
1081 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1085 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1090 //Display is connection not available
1091 //Reflect that in our UI
1092 checkBoxShowVolumeLabel.Enabled = false;
1093 tableLayoutPanel.Enabled = false;
1094 panelDisplay.Enabled = false;
1095 buttonFill.Enabled = false;
1096 buttonClear.Enabled = false;
1097 buttonOpen.Enabled = true;
1098 buttonClose.Enabled = false;
1099 trackBarBrightness.Enabled = false;
1100 buttonPowerOn.Enabled = false;
1101 buttonPowerOff.Enabled = false;
1102 buttonShowClock.Enabled = false;
1103 buttonHideClock.Enabled = false;
1104 toolStripStatusLabelConnect.Text = "Disconnected";
1105 toolStripStatusLabelPower.Text = "N/A";
1114 /// <param name="sender"></param>
1115 /// <param name="e"></param>
1116 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1118 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1119 Properties.Settings.Default.Save();
1123 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1125 //Save our show borders setting
1126 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1127 cds.ShowBorders = checkBoxShowBorders.Checked;
1128 Properties.Settings.Default.Save();
1132 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1134 //Save our connect on startup setting
1135 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1136 Properties.Settings.Default.Save();
1139 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1141 //Save our "Minimize to tray" setting
1142 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1143 Properties.Settings.Default.Save();
1147 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1149 //Save our "Start minimized" setting
1150 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1151 Properties.Settings.Default.Save();
1154 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1156 iStartupManager.Startup = checkBoxAutoStart.Checked;
1160 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1162 //Save our reverse screen setting
1163 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1164 Properties.Settings.Default.Save();
1165 SetupPixelDelegates();
1168 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1170 //Save our inverse colors setting
1171 cds.InverseColors = checkBoxInverseColors.Checked;
1172 Properties.Settings.Default.Save();
1173 SetupPixelDelegates();
1176 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1178 //Save our scale to fit setting
1179 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1180 Properties.Settings.Default.Save();
1182 labelMinFontSize.Enabled = cds.ScaleToFit;
1183 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1186 private void MainForm_Resize(object sender, EventArgs e)
1188 if (WindowState == FormWindowState.Minimized)
1191 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1192 iCreateBitmap = true;
1196 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1198 iNetworkManager.Dispose();
1199 CloseDisplayConnection();
1201 e.Cancel = iClosing;
1204 public void StartServer()
1206 iServiceHost = new ServiceHost
1209 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1212 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1213 iServiceHost.Open();
1216 public void StopServer()
1218 if (iClients.Count > 0 && !iClosing)
1222 BroadcastCloseEvent();
1226 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1228 iClosing = false; //We make sure we force close if asked twice
1233 //We removed that as it often lags for some reason
1234 //iServiceHost.Close();
1238 public void BroadcastCloseEvent()
1240 Trace.TraceInformation("BroadcastCloseEvent - start");
1242 var inactiveClients = new List<string>();
1243 foreach (var client in iClients)
1245 //if (client.Key != eventData.ClientName)
1249 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1250 client.Value.Callback.OnCloseOrder(/*eventData*/);
1252 catch (Exception ex)
1254 inactiveClients.Add(client.Key);
1259 if (inactiveClients.Count > 0)
1261 foreach (var client in inactiveClients)
1263 iClients.Remove(client);
1264 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1268 if (iClients.Count==0)
1275 /// Just remove all our fields.
1277 private void ClearLayout()
1279 tableLayoutPanel.Controls.Clear();
1280 tableLayoutPanel.RowStyles.Clear();
1281 tableLayoutPanel.ColumnStyles.Clear();
1282 iCurrentClientData = null;
1286 /// Just launch a demo client.
1288 private void StartNewClient(string aTopText = "", string aBottomText = "")
1290 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1291 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1292 clientThread.Start(myParams);
1296 private void buttonStartClient_Click(object sender, EventArgs e)
1301 private void buttonSuspend_Click(object sender, EventArgs e)
1303 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1304 timer.Enabled = !timer.Enabled;
1307 buttonSuspend.Text = "Run";
1311 buttonSuspend.Text = "Pause";
1315 private void buttonCloseClients_Click(object sender, EventArgs e)
1317 BroadcastCloseEvent();
1320 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1329 /// <param name="aSessionId"></param>
1330 /// <param name="aCallback"></param>
1331 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1333 if (this.InvokeRequired)
1335 //Not in the proper thread, invoke ourselves
1336 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1337 this.Invoke(d, new object[] { aSessionId, aCallback });
1341 //We are in the proper thread
1342 //Add this session to our collection of clients
1343 ClientData newClient = new ClientData(aSessionId, aCallback);
1344 Program.iMainForm.iClients.Add(aSessionId, newClient);
1345 //Add this session to our UI
1346 UpdateClientTreeViewNode(newClient);
1353 /// <param name="aSessionId"></param>
1354 public void RemoveClientThreadSafe(string aSessionId)
1356 if (this.InvokeRequired)
1358 //Not in the proper thread, invoke ourselves
1359 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1360 this.Invoke(d, new object[] { aSessionId });
1364 //We are in the proper thread
1365 //Remove this session from both client collection and UI tree view
1366 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1368 Program.iMainForm.iClients.Remove(aSessionId);
1369 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1372 if (iClients.Count == 0)
1374 //Clear our screen when last client disconnects
1379 //We were closing our form
1380 //All clients are now closed
1381 //Just resume our close operation
1392 /// <param name="aSessionId"></param>
1393 /// <param name="aLayout"></param>
1394 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1396 if (this.InvokeRequired)
1398 //Not in the proper thread, invoke ourselves
1399 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1400 this.Invoke(d, new object[] { aSessionId, aLayout });
1404 ClientData client = iClients[aSessionId];
1407 client.Layout = aLayout;
1408 UpdateTableLayoutPanel(client);
1410 UpdateClientTreeViewNode(client);
1418 /// <param name="aSessionId"></param>
1419 /// <param name="aField"></param>
1420 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1422 if (this.InvokeRequired)
1424 //Not in the proper thread, invoke ourselves
1425 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1426 this.Invoke(d, new object[] { aSessionId, aField });
1430 //We are in the proper thread
1431 //Call the non-thread-safe variant
1432 SetClientField(aSessionId, aField);
1439 /// <param name="aSessionId"></param>
1440 /// <param name="aField"></param>
1441 private void SetClientField(string aSessionId, DataField aField)
1443 SetCurrentClient(aSessionId);
1444 ClientData client = iClients[aSessionId];
1447 bool somethingChanged = false;
1449 //Make sure all our fields are in place
1450 while (client.Fields.Count < (aField.Index + 1))
1452 //Add a text field with proper index
1453 client.Fields.Add(new DataField(client.Fields.Count));
1454 somethingChanged = true;
1457 if (client.Fields[aField.Index].IsSameLayout(aField))
1459 //Same layout just update our field
1460 client.Fields[aField.Index] = aField;
1462 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1464 //Text field control already in place, just change the text
1465 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1466 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1467 label.Text = aField.Text;
1468 label.TextAlign = aField.Alignment;
1470 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1472 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1473 //Bitmap field control already in place just change the bitmap
1474 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1475 pictureBox.Image = aField.Bitmap;
1479 somethingChanged = true;
1480 //The requested control in our layout it not of the correct type
1481 //Wrong control type, re-create them all
1482 UpdateTableLayoutPanel(iCurrentClientData);
1487 somethingChanged = true;
1488 //Different layout, need to rebuild it
1489 client.Fields[aField.Index] = aField;
1490 UpdateTableLayoutPanel(iCurrentClientData);
1494 if (somethingChanged)
1496 UpdateClientTreeViewNode(client);
1504 /// <param name="aTexts"></param>
1505 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1507 if (this.InvokeRequired)
1509 //Not in the proper thread, invoke ourselves
1510 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1511 this.Invoke(d, new object[] { aSessionId, aFields });
1515 //Put each our text fields in a label control
1516 foreach (DataField field in aFields)
1518 SetClientField(aSessionId, field);
1526 /// <param name="aSessionId"></param>
1527 /// <param name="aName"></param>
1528 public void SetClientNameThreadSafe(string aSessionId, string aName)
1530 if (this.InvokeRequired)
1532 //Not in the proper thread, invoke ourselves
1533 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1534 this.Invoke(d, new object[] { aSessionId, aName });
1538 //We are in the proper thread
1540 ClientData client = iClients[aSessionId];
1544 client.Name = aName;
1545 //Update our tree-view
1546 UpdateClientTreeViewNode(client);
1554 /// <param name="aClient"></param>
1555 private void UpdateClientTreeViewNode(ClientData aClient)
1557 if (aClient == null)
1562 TreeNode node = null;
1563 //Check that our client node already exists
1564 //Get our client root node using its key which is our session ID
1565 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1566 if (nodes.Count()>0)
1568 //We already have a node for that client
1570 //Clear children as we are going to recreate them below
1575 //Client node does not exists create a new one
1576 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1577 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1583 if (aClient.Name != "")
1585 //We have a name, us it as text for our root node
1586 node.Text = aClient.Name;
1587 //Add a child with SessionId
1588 node.Nodes.Add(new TreeNode(aClient.SessionId));
1592 //No name, use session ID instead
1593 node.Text = aClient.SessionId;
1596 if (aClient.Fields.Count > 0)
1598 //Create root node for our texts
1599 TreeNode textsRoot = new TreeNode("Fields");
1600 node.Nodes.Add(textsRoot);
1601 //For each text add a new entry
1602 foreach (DataField field in aClient.Fields)
1604 if (!field.IsBitmap)
1606 DataField textField = (DataField)field;
1607 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1611 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1621 /// Update our table layout row styles to make sure each rows have similar height
1623 private void UpdateTableLayoutRowStyles()
1625 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1627 rowStyle.SizeType = SizeType.Percent;
1628 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1633 /// Update our display table layout.
1635 /// <param name="aLayout"></param>
1636 private void UpdateTableLayoutPanel(ClientData aClient)
1638 if (aClient == null)
1645 TableLayout layout = aClient.Layout;
1648 tableLayoutPanel.Controls.Clear();
1649 tableLayoutPanel.RowStyles.Clear();
1650 tableLayoutPanel.ColumnStyles.Clear();
1651 tableLayoutPanel.RowCount = 0;
1652 tableLayoutPanel.ColumnCount = 0;
1654 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1656 tableLayoutPanel.RowCount++;
1659 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1661 tableLayoutPanel.ColumnCount++;
1664 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1666 //Create our column styles
1667 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1669 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1673 //Create our row styles
1674 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1677 //Check if we already have a control
1678 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1679 if (existingControl!=null)
1681 //We already have a control in that cell as a results of row/col spanning
1682 //Move on to next cell then
1688 //Check if a client field already exists for that cell
1689 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1691 //No client field specified, create a text field by default
1692 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1695 //Create a control corresponding to the field specified for that cell
1696 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1697 Control control = CreateControlForDataField(field);
1699 //Add newly created control to our table layout at the specified row and column
1700 tableLayoutPanel.Controls.Add(control, i, j);
1701 //Make sure we specify row and column span for that new control
1702 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1703 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1708 while (aClient.Fields.Count > fieldCount)
1710 //We have too much fields for this layout
1711 //Just discard them until we get there
1712 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1719 /// Check our type of data field and create corresponding control
1721 /// <param name="aField"></param>
1722 private Control CreateControlForDataField(DataField aField)
1724 Control control=null;
1725 if (!aField.IsBitmap)
1727 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1728 label.AutoEllipsis = true;
1729 label.AutoSize = true;
1730 label.BackColor = System.Drawing.Color.Transparent;
1731 label.Dock = System.Windows.Forms.DockStyle.Fill;
1732 label.Location = new System.Drawing.Point(1, 1);
1733 label.Margin = new System.Windows.Forms.Padding(0);
1734 label.Name = "marqueeLabel" + aField.Index;
1735 label.OwnTimer = false;
1736 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1737 label.Separator = cds.Separator;
1738 label.MinFontSize = cds.MinFontSize;
1739 label.ScaleToFit = cds.ScaleToFit;
1740 //control.Size = new System.Drawing.Size(254, 30);
1741 //control.TabIndex = 2;
1742 label.Font = cds.Font;
1744 label.TextAlign = aField.Alignment;
1745 label.UseCompatibleTextRendering = true;
1746 label.Text = aField.Text;
1752 //Create picture box
1753 PictureBox picture = new PictureBox();
1754 picture.AutoSize = true;
1755 picture.BackColor = System.Drawing.Color.Transparent;
1756 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1757 picture.Location = new System.Drawing.Point(1, 1);
1758 picture.Margin = new System.Windows.Forms.Padding(0);
1759 picture.Name = "pictureBox" + aField;
1761 picture.Image = aField.Bitmap;
1770 /// Called when the user selected a new display type.
1772 /// <param name="sender"></param>
1773 /// <param name="e"></param>
1774 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1776 //Store the selected display type in our settings
1777 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1778 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1779 Properties.Settings.Default.Save();
1781 //Try re-opening the display connection if we were already connected.
1782 //Otherwise just update our status to reflect display type change.
1783 if (iDisplay.IsOpen())
1785 OpenDisplayConnection();
1793 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1795 if (maskedTextBoxTimerInterval.Text != "")
1797 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1801 timer.Interval = interval;
1802 cds.TimerInterval = timer.Interval;
1803 Properties.Settings.Default.Save();
1808 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1810 if (maskedTextBoxMinFontSize.Text != "")
1812 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1814 if (minFontSize > 0)
1816 cds.MinFontSize = minFontSize;
1817 Properties.Settings.Default.Save();
1818 //We need to recreate our layout for that change to take effect
1819 UpdateTableLayoutPanel(iCurrentClientData);
1825 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1827 if (maskedTextBoxScrollingSpeed.Text != "")
1829 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1831 if (scrollingSpeed > 0)
1833 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1834 Properties.Settings.Default.Save();
1835 //We need to recreate our layout for that change to take effect
1836 UpdateTableLayoutPanel(iCurrentClientData);
1841 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1843 cds.Separator = textBoxScrollLoopSeparator.Text;
1844 Properties.Settings.Default.Save();
1846 //Update our text fields
1847 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1849 ctrl.Separator = cds.Separator;
1854 private void buttonPowerOn_Click(object sender, EventArgs e)
1859 private void buttonPowerOff_Click(object sender, EventArgs e)
1861 iDisplay.PowerOff();
1864 private void buttonShowClock_Click(object sender, EventArgs e)
1869 private void buttonHideClock_Click(object sender, EventArgs e)
1874 private void buttonUpdate_Click(object sender, EventArgs e)
1876 InstallUpdateSyncWithInfo();
1884 if (!iDisplay.IsOpen())
1889 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1890 iSkipFrameRendering = true;
1893 iDisplay.SwapBuffers();
1894 //Then show our clock
1895 iDisplay.ShowClock();
1903 if (!iDisplay.IsOpen())
1908 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1909 iSkipFrameRendering = false;
1910 iDisplay.HideClock();
1913 private void InstallUpdateSyncWithInfo()
1915 UpdateCheckInfo info = null;
1917 if (ApplicationDeployment.IsNetworkDeployed)
1919 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1923 info = ad.CheckForDetailedUpdate();
1926 catch (DeploymentDownloadException dde)
1928 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);
1931 catch (InvalidDeploymentException ide)
1933 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);
1936 catch (InvalidOperationException ioe)
1938 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1942 if (info.UpdateAvailable)
1944 Boolean doUpdate = true;
1946 if (!info.IsUpdateRequired)
1948 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1949 if (!(DialogResult.OK == dr))
1956 // Display a message that the app MUST reboot. Display the minimum required version.
1957 MessageBox.Show("This application has detected a mandatory update from your current " +
1958 "version to version " + info.MinimumRequiredVersion.ToString() +
1959 ". The application will now install the update and restart.",
1960 "Update Available", MessageBoxButtons.OK,
1961 MessageBoxIcon.Information);
1969 MessageBox.Show("The application has been upgraded, and will now restart.");
1970 Application.Restart();
1972 catch (DeploymentDownloadException dde)
1974 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1981 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1990 private void SysTrayHideShow()
1996 WindowState = FormWindowState.Normal;
2001 /// Use to handle minimize events.
2003 /// <param name="sender"></param>
2004 /// <param name="e"></param>
2005 private void MainForm_SizeChanged(object sender, EventArgs e)
2007 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2020 /// <param name="sender"></param>
2021 /// <param name="e"></param>
2022 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2024 //Our table layout size has changed which means our display size has changed.
2025 //We need to re-create our bitmap.
2026 iCreateBitmap = true;
2032 /// <param name="sender"></param>
2033 /// <param name="e"></param>
2034 private void buttonSelectFile_Click(object sender, EventArgs e)
2036 //openFileDialog1.InitialDirectory = "c:\\";
2037 //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
2038 //openFileDialog.FilterIndex = 1;
2039 openFileDialog.RestoreDirectory = true;
2041 if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
2043 labelStartFileName.Text = openFileDialog.FileName;
2044 Properties.Settings.Default.StartFileName = openFileDialog.FileName;
2045 Properties.Settings.Default.Save();
2051 /// A UI thread copy of a client relevant data.
2052 /// Keeping this copy in the UI thread helps us deal with threading issues.
2054 public class ClientData
2056 public ClientData(string aSessionId, ICallback aCallback)
2058 SessionId = aSessionId;
2060 Fields = new List<DataField>();
2061 Layout = new TableLayout(1, 2); //Default to one column and two rows
2062 Callback = aCallback;
2065 public string SessionId { get; set; }
2066 public string Name { get; set; }
2067 public List<DataField> Fields { get; set; }
2068 public TableLayout Layout { get; set; }
2069 public ICallback Callback { get; set; }