Liscense and Copyright fix.
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;
47 namespace SharpDisplayManager
50 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
51 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
52 //Delegates are used for our thread safe method
53 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
54 public delegate void RemoveClientDelegate(string aSessionId);
55 public delegate void SetFieldDelegate(string SessionId, DataField aField);
56 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
57 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
58 public delegate void SetClientNameDelegate(string aSessionId, string aName);
59 public delegate void PlainUpdateDelegate();
63 /// Our Display manager main form
65 public partial class MainForm : Form, IMMNotificationClient
67 DateTime LastTickTime;
69 System.Drawing.Bitmap iBmp;
70 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
71 ServiceHost iServiceHost;
72 // Our collection of clients sorted by session id.
73 public Dictionary<string, ClientData> iClients;
74 // The name of the client which informations are currently displayed.
75 public string iCurrentClientSessionId;
76 ClientData iCurrentClientData;
80 public bool iSkipFrameRendering;
81 //Function pointer for pixel color filtering
82 ColorProcessingDelegate iColorFx;
83 //Function pointer for pixel X coordinate intercept
84 CoordinateTranslationDelegate iScreenX;
85 //Function pointer for pixel Y coordinate intercept
86 CoordinateTranslationDelegate iScreenY;
88 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
89 private MMDevice iMultiMediaDevice;
91 private NetworkManager iNetworkManager;
95 /// Manage run when Windows startup option
97 private StartupManager iStartupManager;
100 /// System tray icon.
102 private NotifyIconAdv iNotifyIcon;
106 iSkipFrameRendering = false;
108 iCurrentClientSessionId = "";
109 iCurrentClientData = null;
110 LastTickTime = DateTime.Now;
111 //Instantiate our display and register for events notifications
112 iDisplay = new Display();
113 iDisplay.OnOpened += OnDisplayOpened;
114 iDisplay.OnClosed += OnDisplayClosed;
116 iClients = new Dictionary<string, ClientData>();
117 iStartupManager = new StartupManager();
118 iNotifyIcon = new NotifyIconAdv();
120 //Have our designer initialize its controls
121 InitializeComponent();
123 //Populate device types
124 PopulateDeviceTypes();
126 //Initial status update
129 //We have a bug when drawing minimized and reusing our bitmap
130 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
131 iCreateBitmap = false;
133 //Minimize our window if desired
134 if (Properties.Settings.Default.StartMinimized)
136 WindowState = FormWindowState.Minimized;
144 /// <param name="sender"></param>
145 /// <param name="e"></param>
146 private void MainForm_Load(object sender, EventArgs e)
148 //Check if we are running a Click Once deployed application
149 if (ApplicationDeployment.IsNetworkDeployed)
151 //This is a proper Click Once installation, fetch and show our version number
152 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
156 //Not a proper Click Once installation, assuming development build then
157 this.Text += " - development";
161 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
162 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
163 UpdateAudioDeviceAndMasterVolumeThreadSafe();
166 iNetworkManager = new NetworkManager();
167 iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
168 UpdateNetworkStatus();
170 //Setup notification icon
173 // To make sure start up with minimize to tray works
174 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
180 //When not debugging we want the screen to be empty until a client takes over
183 //When developing we want at least one client for testing
184 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
187 //Open display connection on start-up if needed
188 if (Properties.Settings.Default.DisplayConnectOnStartup)
190 OpenDisplayConnection();
193 //Start our server so that we can get client requests
198 /// Called when our display is opened.
200 /// <param name="aDisplay"></param>
201 private void OnDisplayOpened(Display aDisplay)
203 //Make sure we resume frame rendering
204 iSkipFrameRendering = false;
206 //Set our screen size now that our display is connected
207 //Our panelDisplay is the container of our tableLayoutPanel
208 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
209 //panelDisplay needs an extra 2 pixels for borders on each sides
210 //tableLayoutPanel will eventually be the exact size of our display
211 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
212 panelDisplay.Size = size;
214 //Our display was just opened, update our UI
216 //Initiate asynchronous request
217 iDisplay.RequestFirmwareRevision();
220 UpdateMasterVolumeThreadSafe();
222 UpdateNetworkStatus();
225 //Testing icon in debug, no arm done if icon not supported
226 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
227 //iDisplay.SetAllIconsStatus(2);
233 /// Called when our display is closed.
235 /// <param name="aDisplay"></param>
236 private void OnDisplayClosed(Display aDisplay)
238 //Our display was just closed, update our UI consequently
242 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
244 //Update network status
245 UpdateNetworkStatus();
249 /// Update our Network Status
251 private void UpdateNetworkStatus()
253 if (iDisplay.IsOpen())
255 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconInternet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
256 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
261 int iLastNetworkIconIndex = 0;
262 int iUpdateCountSinceLastNetworkAnimation = 0;
267 private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
269 iUpdateCountSinceLastNetworkAnimation++;
270 iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
272 if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
274 int iconCount=iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal);
277 //Prevents div by zero and other undefined behavior
280 iLastNetworkIconIndex++;
281 iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2);
282 for (int i=0;i<iconCount;i++)
284 if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && !(i == 1 && iLastNetworkIconIndex > 4))
286 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
290 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
299 /// Receive volume change notification and reflect changes on our slider.
301 /// <param name="data"></param>
302 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
304 UpdateMasterVolumeThreadSafe();
308 /// Update master volume when user moves our slider.
310 /// <param name="sender"></param>
311 /// <param name="e"></param>
312 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
314 //Just like Windows Volume Mixer we unmute if the volume is adjusted
315 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
316 //Set volume level according to our volume slider new position
317 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
322 /// Mute check box changed.
324 /// <param name="sender"></param>
325 /// <param name="e"></param>
326 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
328 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
332 /// Device State Changed
334 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
339 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
344 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
347 /// Default Device Changed
349 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
351 if (role == Role.Multimedia && flow == DataFlow.Render)
353 UpdateAudioDeviceAndMasterVolumeThreadSafe();
358 /// Property Value Changed
360 /// <param name="pwstrDeviceId"></param>
361 /// <param name="key"></param>
362 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
368 /// Update master volume indicators based our current system states.
369 /// This typically includes volume levels and mute status.
371 private void UpdateMasterVolumeThreadSafe()
373 if (this.InvokeRequired)
375 //Not in the proper thread, invoke ourselves
376 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
377 this.Invoke(d, new object[] { });
381 //Update volume slider
382 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
383 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
384 //Update mute checkbox
385 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
387 //If our display connection is open we need to update its icons
388 if (iDisplay.IsOpen())
390 //First take care our our volume level icons
391 int volumeIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume);
392 if (volumeIconCount > 0)
394 //Compute current volume level from system level and the number of segments in our display volume bar.
395 //That tells us how many segments in our volume bar needs to be turned on.
396 float currentVolume = volumeLevelScalar * volumeIconCount;
397 int segmentOnCount = Convert.ToInt32(currentVolume);
398 //Check if our segment count was rounded up, this will later be used for half brightness segment
399 bool roundedUp = segmentOnCount > currentVolume;
401 for (int i = 0; i < volumeIconCount; i++)
403 if (i < segmentOnCount)
405 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
406 if (i == segmentOnCount - 1 && roundedUp)
409 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, (iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1)/2);
414 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1);
419 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 0);
424 //Take care our our mute icon
425 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconMute, iMultiMediaDevice.AudioEndpointVolume.Mute);
433 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
435 if (this.InvokeRequired)
437 //Not in the proper thread, invoke ourselves
438 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
439 this.Invoke(d, new object[] { });
443 //We are in the correct thread just go ahead.
446 //Get our master volume
447 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
449 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
451 //Show our volume in our track bar
452 UpdateMasterVolumeThreadSafe();
454 //Register to get volume modifications
455 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
457 trackBarMasterVolume.Enabled = true;
461 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
462 Debug.WriteLine(ex.ToString());
463 //Something went wrong S/PDIF device ca throw exception I guess
464 trackBarMasterVolume.Enabled = false;
471 private void PopulateDeviceTypes()
473 int count = Display.TypeCount();
475 for (int i = 0; i < count; i++)
477 comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
484 private void SetupTrayIcon()
486 iNotifyIcon.Icon = GetIcon("vfd.ico");
487 iNotifyIcon.Text = "Sharp Display Manager";
488 iNotifyIcon.Visible = true;
490 //Double click toggles visibility - typically brings up the application
491 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
496 //Adding a context menu, useful to be able to exit the application
497 ContextMenu contextMenu = new ContextMenu();
498 //Context menu item to toggle visibility
499 MenuItem hideShowItem = new MenuItem("Hide/Show");
500 hideShowItem.Click += delegate(object obj, EventArgs args)
504 contextMenu.MenuItems.Add(hideShowItem);
506 //Context menu item separator
507 contextMenu.MenuItems.Add(new MenuItem("-"));
509 //Context menu exit item
510 MenuItem exitItem = new MenuItem("Exit");
511 exitItem.Click += delegate(object obj, EventArgs args)
515 contextMenu.MenuItems.Add(exitItem);
517 iNotifyIcon.ContextMenu = contextMenu;
521 /// Access icons from embedded resources.
523 /// <param name="name"></param>
524 /// <returns></returns>
525 public static Icon GetIcon(string name)
527 name = "SharpDisplayManager.Resources." + name;
530 Assembly.GetExecutingAssembly().GetManifestResourceNames();
531 for (int i = 0; i < names.Length; i++)
533 if (names[i].Replace('\\', '.') == name)
535 using (Stream stream = Assembly.GetExecutingAssembly().
536 GetManifestResourceStream(names[i]))
538 return new Icon(stream);
548 /// Set our current client.
549 /// This will take care of applying our client layout and set data fields.
551 /// <param name="aSessionId"></param>
552 void SetCurrentClient(string aSessionId)
554 if (aSessionId == iCurrentClientSessionId)
556 //Given client is already the current one.
557 //Don't bother changing anything then.
561 //Set current client ID.
562 iCurrentClientSessionId = aSessionId;
563 //Fetch and set current client data.
564 iCurrentClientData = iClients[aSessionId];
565 //Apply layout and set data fields.
566 UpdateTableLayoutPanel(iCurrentClientData);
569 private void buttonFont_Click(object sender, EventArgs e)
571 //fontDialog.ShowColor = true;
572 //fontDialog.ShowApply = true;
573 fontDialog.ShowEffects = true;
574 fontDialog.Font = cds.Font;
576 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
578 //fontDialog.ShowHelp = true;
580 //fontDlg.MaxSize = 40;
581 //fontDlg.MinSize = 22;
583 //fontDialog.Parent = this;
584 //fontDialog.StartPosition = FormStartPosition.CenterParent;
586 //DlgBox.ShowDialog(fontDialog);
588 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
589 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
591 //Set the fonts to all our labels in our layout
592 foreach (Control ctrl in tableLayoutPanel.Controls)
594 if (ctrl is MarqueeLabel)
596 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
601 cds.Font = fontDialog.Font;
602 Properties.Settings.Default.Save();
611 void CheckFontHeight()
613 //Show font height and width
614 labelFontHeight.Text = "Font height: " + cds.Font.Height;
615 float charWidth = IsFixedWidth(cds.Font);
616 if (charWidth == 0.0f)
618 labelFontWidth.Visible = false;
622 labelFontWidth.Visible = true;
623 labelFontWidth.Text = "Font width: " + charWidth;
626 MarqueeLabel label = null;
627 //Get the first label control we can find
628 foreach (Control ctrl in tableLayoutPanel.Controls)
630 if (ctrl is MarqueeLabel)
632 label = (MarqueeLabel)ctrl;
637 //Now check font height and show a warning if needed.
638 if (label != null && label.Font.Height > label.Height)
640 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
641 labelWarning.Visible = true;
645 labelWarning.Visible = false;
650 private void buttonCapture_Click(object sender, EventArgs e)
652 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
653 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
654 //Bitmap bmpToSave = new Bitmap(bmp);
655 bmp.Save("D:\\capture.png");
657 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
660 string outputFileName = "d:\\capture.png";
661 using (MemoryStream memory = new MemoryStream())
663 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
665 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
666 byte[] bytes = memory.ToArray();
667 fs.Write(bytes, 0, bytes.Length);
674 private void CheckForRequestResults()
676 if (iDisplay.IsRequestPending())
678 switch (iDisplay.AttemptRequestCompletion())
680 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
681 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
682 //Issue next request then
683 iDisplay.RequestPowerSupplyStatus();
686 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
687 if (iDisplay.PowerSupplyStatus())
689 toolStripStatusLabelPower.Text = "ON";
693 toolStripStatusLabelPower.Text = "OFF";
695 //Issue next request then
696 iDisplay.RequestDeviceId();
699 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
700 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
701 //No more request to issue
707 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
709 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
716 public static uint ColorUntouched(int aX, int aY, uint aPixel)
721 public static uint ColorInversed(int aX, int aY, uint aPixel)
726 public static uint ColorChessboard(int aX, int aY, uint aPixel)
728 if ((aX % 2 == 0) && (aY % 2 == 0))
732 else if ((aX % 2 != 0) && (aY % 2 != 0))
740 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
742 return aBmp.Width - aX - 1;
745 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
747 return iBmp.Height - aY - 1;
750 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
755 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
761 /// Select proper pixel delegates according to our current settings.
763 private void SetupPixelDelegates()
765 //Select our pixel processing routine
766 if (cds.InverseColors)
768 //iColorFx = ColorChessboard;
769 iColorFx = ColorInversed;
773 iColorFx = ColorWhiteIsOn;
776 //Select proper coordinate translation functions
777 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
778 if (cds.ReverseScreen)
780 iScreenX = ScreenReversedX;
781 iScreenY = ScreenReversedY;
791 //This is our timer tick responsible to perform our render
792 private void timer_Tick(object sender, EventArgs e)
794 //Update our animations
795 DateTime NewTickTime = DateTime.Now;
797 UpdateNetworkSignal(LastTickTime, NewTickTime);
799 //Update animation for all our marquees
800 foreach (Control ctrl in tableLayoutPanel.Controls)
802 if (ctrl is MarqueeLabel)
804 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
809 if (iDisplay.IsOpen())
811 CheckForRequestResults();
813 //Check if frame rendering is needed
814 //Typically used when showing clock
815 if (!iSkipFrameRendering)
820 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
822 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
823 //iBmp.Save("D:\\capture.png");
825 //Send it to our display
826 for (int i = 0; i < iBmp.Width; i++)
828 for (int j = 0; j < iBmp.Height; j++)
832 //Get our processed pixel coordinates
833 int x = iScreenX(iBmp, i);
834 int y = iScreenY(iBmp, j);
836 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
837 //Apply color effects
838 color = iColorFx(x, y, color);
840 iDisplay.SetPixel(x, y, color);
845 iDisplay.SwapBuffers();
849 //Compute instant FPS
850 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
852 LastTickTime = NewTickTime;
857 /// Attempt to establish connection with our display hardware.
859 private void OpenDisplayConnection()
861 CloseDisplayConnection();
863 if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
866 toolStripStatusLabelConnect.Text = "Connection error";
870 private void CloseDisplayConnection()
872 //Status will be updated upon receiving the closed event
874 if (iDisplay == null || !iDisplay.IsOpen())
879 //Do not clear if we gave up on rendering already.
880 //This means we will keep on displaying clock on MDM166AA for instance.
881 if (!iSkipFrameRendering)
884 iDisplay.SwapBuffers();
887 iDisplay.SetAllIconsStatus(0); //Turn off all icons
891 private void buttonOpen_Click(object sender, EventArgs e)
893 OpenDisplayConnection();
896 private void buttonClose_Click(object sender, EventArgs e)
898 CloseDisplayConnection();
901 private void buttonClear_Click(object sender, EventArgs e)
904 iDisplay.SwapBuffers();
907 private void buttonFill_Click(object sender, EventArgs e)
910 iDisplay.SwapBuffers();
913 private void trackBarBrightness_Scroll(object sender, EventArgs e)
915 cds.Brightness = trackBarBrightness.Value;
916 Properties.Settings.Default.Save();
917 iDisplay.SetBrightness(trackBarBrightness.Value);
923 /// CDS stands for Current Display Settings
925 private DisplaySettings cds
929 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
930 if (settings == null)
932 settings = new DisplaysSettings();
934 Properties.Settings.Default.DisplaysSettings = settings;
937 //Make sure all our settings have been created
938 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
940 settings.Displays.Add(new DisplaySettings());
943 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
944 return displaySettings;
949 /// Check if the given font has a fixed character pitch.
951 /// <param name="ft"></param>
952 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
953 public float IsFixedWidth(Font ft)
955 Graphics g = CreateGraphics();
956 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
957 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
959 bool fixedWidth = true;
961 foreach (char c in charSizes)
962 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
974 /// Synchronize UI with settings
976 private void UpdateStatus()
979 checkBoxShowBorders.Checked = cds.ShowBorders;
980 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
982 //Set the proper font to each of our labels
983 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
985 ctrl.Font = cds.Font;
989 //Check if "run on Windows startup" is enabled
990 checkBoxAutoStart.Checked = iStartupManager.Startup;
992 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
993 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
994 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
995 checkBoxReverseScreen.Checked = cds.ReverseScreen;
996 checkBoxInverseColors.Checked = cds.InverseColors;
997 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
998 checkBoxScaleToFit.Checked = cds.ScaleToFit;
999 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1000 labelMinFontSize.Enabled = cds.ScaleToFit;
1001 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
1002 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
1003 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
1004 timer.Interval = cds.TimerInterval;
1005 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
1006 textBoxScrollLoopSeparator.Text = cds.Separator;
1008 SetupPixelDelegates();
1010 if (iDisplay.IsOpen())
1012 //We have a display connection
1013 //Reflect that in our UI
1015 tableLayoutPanel.Enabled = true;
1016 panelDisplay.Enabled = true;
1018 //Only setup brightness if display is open
1019 trackBarBrightness.Minimum = iDisplay.MinBrightness();
1020 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
1021 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
1023 //Brightness out of range, this can occur when using auto-detect
1024 //Use max brightness instead
1025 trackBarBrightness.Value = iDisplay.MaxBrightness();
1026 iDisplay.SetBrightness(iDisplay.MaxBrightness());
1030 trackBarBrightness.Value = cds.Brightness;
1031 iDisplay.SetBrightness(cds.Brightness);
1034 //Try compute the steps to something that makes sense
1035 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
1036 trackBarBrightness.SmallChange = 1;
1039 buttonFill.Enabled = true;
1040 buttonClear.Enabled = true;
1041 buttonOpen.Enabled = false;
1042 buttonClose.Enabled = true;
1043 trackBarBrightness.Enabled = true;
1044 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1045 //+ " - " + iDisplay.SerialNumber();
1047 if (iDisplay.SupportPowerOnOff())
1049 buttonPowerOn.Enabled = true;
1050 buttonPowerOff.Enabled = true;
1054 buttonPowerOn.Enabled = false;
1055 buttonPowerOff.Enabled = false;
1058 if (iDisplay.SupportClock())
1060 buttonShowClock.Enabled = true;
1061 buttonHideClock.Enabled = true;
1065 buttonShowClock.Enabled = false;
1066 buttonHideClock.Enabled = false;
1070 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1071 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel)>0;
1073 if (cds.ShowVolumeLabel)
1075 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1079 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1084 //Display is connection not available
1085 //Reflect that in our UI
1086 checkBoxShowVolumeLabel.Enabled = false;
1087 tableLayoutPanel.Enabled = false;
1088 panelDisplay.Enabled = false;
1089 buttonFill.Enabled = false;
1090 buttonClear.Enabled = false;
1091 buttonOpen.Enabled = true;
1092 buttonClose.Enabled = false;
1093 trackBarBrightness.Enabled = false;
1094 buttonPowerOn.Enabled = false;
1095 buttonPowerOff.Enabled = false;
1096 buttonShowClock.Enabled = false;
1097 buttonHideClock.Enabled = false;
1098 toolStripStatusLabelConnect.Text = "Disconnected";
1099 toolStripStatusLabelPower.Text = "N/A";
1108 /// <param name="sender"></param>
1109 /// <param name="e"></param>
1110 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1112 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1113 Properties.Settings.Default.Save();
1117 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1119 //Save our show borders setting
1120 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1121 cds.ShowBorders = checkBoxShowBorders.Checked;
1122 Properties.Settings.Default.Save();
1126 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1128 //Save our connect on startup setting
1129 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1130 Properties.Settings.Default.Save();
1133 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1135 //Save our "Minimize to tray" setting
1136 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1137 Properties.Settings.Default.Save();
1141 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1143 //Save our "Start minimized" setting
1144 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1145 Properties.Settings.Default.Save();
1148 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1150 iStartupManager.Startup = checkBoxAutoStart.Checked;
1154 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1156 //Save our reverse screen setting
1157 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1158 Properties.Settings.Default.Save();
1159 SetupPixelDelegates();
1162 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1164 //Save our inverse colors setting
1165 cds.InverseColors = checkBoxInverseColors.Checked;
1166 Properties.Settings.Default.Save();
1167 SetupPixelDelegates();
1170 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1172 //Save our scale to fit setting
1173 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1174 Properties.Settings.Default.Save();
1176 labelMinFontSize.Enabled = cds.ScaleToFit;
1177 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1180 private void MainForm_Resize(object sender, EventArgs e)
1182 if (WindowState == FormWindowState.Minimized)
1185 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1186 iCreateBitmap = true;
1190 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1192 iNetworkManager.Dispose();
1193 CloseDisplayConnection();
1195 e.Cancel = iClosing;
1198 public void StartServer()
1200 iServiceHost = new ServiceHost
1203 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1206 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1207 iServiceHost.Open();
1210 public void StopServer()
1212 if (iClients.Count > 0 && !iClosing)
1216 BroadcastCloseEvent();
1220 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1222 iClosing = false; //We make sure we force close if asked twice
1227 //We removed that as it often lags for some reason
1228 //iServiceHost.Close();
1232 public void BroadcastCloseEvent()
1234 Trace.TraceInformation("BroadcastCloseEvent - start");
1236 var inactiveClients = new List<string>();
1237 foreach (var client in iClients)
1239 //if (client.Key != eventData.ClientName)
1243 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1244 client.Value.Callback.OnCloseOrder(/*eventData*/);
1246 catch (Exception ex)
1248 inactiveClients.Add(client.Key);
1253 if (inactiveClients.Count > 0)
1255 foreach (var client in inactiveClients)
1257 iClients.Remove(client);
1258 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1262 if (iClients.Count==0)
1269 /// Just remove all our fields.
1271 private void ClearLayout()
1273 tableLayoutPanel.Controls.Clear();
1274 tableLayoutPanel.RowStyles.Clear();
1275 tableLayoutPanel.ColumnStyles.Clear();
1276 iCurrentClientData = null;
1280 /// Just launch a demo client.
1282 private void StartNewClient(string aTopText = "", string aBottomText = "")
1284 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1285 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1286 clientThread.Start(myParams);
1290 private void buttonStartClient_Click(object sender, EventArgs e)
1295 private void buttonSuspend_Click(object sender, EventArgs e)
1297 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1298 timer.Enabled = !timer.Enabled;
1301 buttonSuspend.Text = "Run";
1305 buttonSuspend.Text = "Pause";
1309 private void buttonCloseClients_Click(object sender, EventArgs e)
1311 BroadcastCloseEvent();
1314 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1323 /// <param name="aSessionId"></param>
1324 /// <param name="aCallback"></param>
1325 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1327 if (this.InvokeRequired)
1329 //Not in the proper thread, invoke ourselves
1330 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1331 this.Invoke(d, new object[] { aSessionId, aCallback });
1335 //We are in the proper thread
1336 //Add this session to our collection of clients
1337 ClientData newClient = new ClientData(aSessionId, aCallback);
1338 Program.iMainForm.iClients.Add(aSessionId, newClient);
1339 //Add this session to our UI
1340 UpdateClientTreeViewNode(newClient);
1347 /// <param name="aSessionId"></param>
1348 public void RemoveClientThreadSafe(string aSessionId)
1350 if (this.InvokeRequired)
1352 //Not in the proper thread, invoke ourselves
1353 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1354 this.Invoke(d, new object[] { aSessionId });
1358 //We are in the proper thread
1359 //Remove this session from both client collection and UI tree view
1360 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1362 Program.iMainForm.iClients.Remove(aSessionId);
1363 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1366 if (iClients.Count == 0)
1368 //Clear our screen when last client disconnects
1373 //We were closing our form
1374 //All clients are now closed
1375 //Just resume our close operation
1386 /// <param name="aSessionId"></param>
1387 /// <param name="aLayout"></param>
1388 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1390 if (this.InvokeRequired)
1392 //Not in the proper thread, invoke ourselves
1393 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1394 this.Invoke(d, new object[] { aSessionId, aLayout });
1398 ClientData client = iClients[aSessionId];
1401 client.Layout = aLayout;
1402 UpdateTableLayoutPanel(client);
1404 UpdateClientTreeViewNode(client);
1412 /// <param name="aSessionId"></param>
1413 /// <param name="aField"></param>
1414 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1416 if (this.InvokeRequired)
1418 //Not in the proper thread, invoke ourselves
1419 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1420 this.Invoke(d, new object[] { aSessionId, aField });
1424 //We are in the proper thread
1425 //Call the non-thread-safe variant
1426 SetClientField(aSessionId, aField);
1433 /// <param name="aSessionId"></param>
1434 /// <param name="aField"></param>
1435 private void SetClientField(string aSessionId, DataField aField)
1437 SetCurrentClient(aSessionId);
1438 ClientData client = iClients[aSessionId];
1441 bool somethingChanged = false;
1443 //Make sure all our fields are in place
1444 while (client.Fields.Count < (aField.Index + 1))
1446 //Add a text field with proper index
1447 client.Fields.Add(new DataField(client.Fields.Count));
1448 somethingChanged = true;
1451 if (client.Fields[aField.Index].IsSameLayout(aField))
1453 //Same layout just update our field
1454 client.Fields[aField.Index] = aField;
1456 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1458 //Text field control already in place, just change the text
1459 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1460 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1461 label.Text = aField.Text;
1462 label.TextAlign = aField.Alignment;
1464 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1466 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1467 //Bitmap field control already in place just change the bitmap
1468 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1469 pictureBox.Image = aField.Bitmap;
1473 somethingChanged = true;
1474 //The requested control in our layout it not of the correct type
1475 //Wrong control type, re-create them all
1476 UpdateTableLayoutPanel(iCurrentClientData);
1481 somethingChanged = true;
1482 //Different layout, need to rebuild it
1483 client.Fields[aField.Index] = aField;
1484 UpdateTableLayoutPanel(iCurrentClientData);
1488 if (somethingChanged)
1490 UpdateClientTreeViewNode(client);
1498 /// <param name="aTexts"></param>
1499 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1501 if (this.InvokeRequired)
1503 //Not in the proper thread, invoke ourselves
1504 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1505 this.Invoke(d, new object[] { aSessionId, aFields });
1509 //Put each our text fields in a label control
1510 foreach (DataField field in aFields)
1512 SetClientField(aSessionId, field);
1520 /// <param name="aSessionId"></param>
1521 /// <param name="aName"></param>
1522 public void SetClientNameThreadSafe(string aSessionId, string aName)
1524 if (this.InvokeRequired)
1526 //Not in the proper thread, invoke ourselves
1527 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1528 this.Invoke(d, new object[] { aSessionId, aName });
1532 //We are in the proper thread
1534 ClientData client = iClients[aSessionId];
1538 client.Name = aName;
1539 //Update our tree-view
1540 UpdateClientTreeViewNode(client);
1548 /// <param name="aClient"></param>
1549 private void UpdateClientTreeViewNode(ClientData aClient)
1551 if (aClient == null)
1556 TreeNode node = null;
1557 //Check that our client node already exists
1558 //Get our client root node using its key which is our session ID
1559 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1560 if (nodes.Count()>0)
1562 //We already have a node for that client
1564 //Clear children as we are going to recreate them below
1569 //Client node does not exists create a new one
1570 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1571 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1577 if (aClient.Name != "")
1579 //We have a name, us it as text for our root node
1580 node.Text = aClient.Name;
1581 //Add a child with SessionId
1582 node.Nodes.Add(new TreeNode(aClient.SessionId));
1586 //No name, use session ID instead
1587 node.Text = aClient.SessionId;
1590 if (aClient.Fields.Count > 0)
1592 //Create root node for our texts
1593 TreeNode textsRoot = new TreeNode("Fields");
1594 node.Nodes.Add(textsRoot);
1595 //For each text add a new entry
1596 foreach (DataField field in aClient.Fields)
1598 if (!field.IsBitmap)
1600 DataField textField = (DataField)field;
1601 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1605 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1615 /// Update our table layout row styles to make sure each rows have similar height
1617 private void UpdateTableLayoutRowStyles()
1619 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1621 rowStyle.SizeType = SizeType.Percent;
1622 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1627 /// Update our display table layout.
1629 /// <param name="aLayout"></param>
1630 private void UpdateTableLayoutPanel(ClientData aClient)
1632 if (aClient == null)
1639 TableLayout layout = aClient.Layout;
1642 tableLayoutPanel.Controls.Clear();
1643 tableLayoutPanel.RowStyles.Clear();
1644 tableLayoutPanel.ColumnStyles.Clear();
1645 tableLayoutPanel.RowCount = 0;
1646 tableLayoutPanel.ColumnCount = 0;
1648 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1650 tableLayoutPanel.RowCount++;
1653 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1655 tableLayoutPanel.ColumnCount++;
1658 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1660 //Create our column styles
1661 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1663 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1667 //Create our row styles
1668 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1671 //Check if we already have a control
1672 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1673 if (existingControl!=null)
1675 //We already have a control in that cell as a results of row/col spanning
1676 //Move on to next cell then
1682 //Check if a client field already exists for that cell
1683 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1685 //No client field specified, create a text field by default
1686 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1689 //Create a control corresponding to the field specified for that cell
1690 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1691 Control control = CreateControlForDataField(field);
1693 //Add newly created control to our table layout at the specified row and column
1694 tableLayoutPanel.Controls.Add(control, i, j);
1695 //Make sure we specify row and column span for that new control
1696 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1697 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1702 while (aClient.Fields.Count > fieldCount)
1704 //We have too much fields for this layout
1705 //Just discard them until we get there
1706 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1713 /// Check our type of data field and create corresponding control
1715 /// <param name="aField"></param>
1716 private Control CreateControlForDataField(DataField aField)
1718 Control control=null;
1719 if (!aField.IsBitmap)
1721 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1722 label.AutoEllipsis = true;
1723 label.AutoSize = true;
1724 label.BackColor = System.Drawing.Color.Transparent;
1725 label.Dock = System.Windows.Forms.DockStyle.Fill;
1726 label.Location = new System.Drawing.Point(1, 1);
1727 label.Margin = new System.Windows.Forms.Padding(0);
1728 label.Name = "marqueeLabel" + aField.Index;
1729 label.OwnTimer = false;
1730 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1731 label.Separator = cds.Separator;
1732 label.MinFontSize = cds.MinFontSize;
1733 label.ScaleToFit = cds.ScaleToFit;
1734 //control.Size = new System.Drawing.Size(254, 30);
1735 //control.TabIndex = 2;
1736 label.Font = cds.Font;
1738 label.TextAlign = aField.Alignment;
1739 label.UseCompatibleTextRendering = true;
1740 label.Text = aField.Text;
1746 //Create picture box
1747 PictureBox picture = new PictureBox();
1748 picture.AutoSize = true;
1749 picture.BackColor = System.Drawing.Color.Transparent;
1750 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1751 picture.Location = new System.Drawing.Point(1, 1);
1752 picture.Margin = new System.Windows.Forms.Padding(0);
1753 picture.Name = "pictureBox" + aField;
1755 picture.Image = aField.Bitmap;
1764 /// Called when the user selected a new display type.
1766 /// <param name="sender"></param>
1767 /// <param name="e"></param>
1768 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1770 //Store the selected display type in our settings
1771 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1772 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1773 Properties.Settings.Default.Save();
1775 //Try re-opening the display connection if we were already connected.
1776 //Otherwise just update our status to reflect display type change.
1777 if (iDisplay.IsOpen())
1779 OpenDisplayConnection();
1787 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1789 if (maskedTextBoxTimerInterval.Text != "")
1791 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1795 timer.Interval = interval;
1796 cds.TimerInterval = timer.Interval;
1797 Properties.Settings.Default.Save();
1802 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1804 if (maskedTextBoxMinFontSize.Text != "")
1806 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1808 if (minFontSize > 0)
1810 cds.MinFontSize = minFontSize;
1811 Properties.Settings.Default.Save();
1812 //We need to recreate our layout for that change to take effect
1813 UpdateTableLayoutPanel(iCurrentClientData);
1819 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1821 if (maskedTextBoxScrollingSpeed.Text != "")
1823 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1825 if (scrollingSpeed > 0)
1827 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1828 Properties.Settings.Default.Save();
1829 //We need to recreate our layout for that change to take effect
1830 UpdateTableLayoutPanel(iCurrentClientData);
1835 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1837 cds.Separator = textBoxScrollLoopSeparator.Text;
1838 Properties.Settings.Default.Save();
1840 //Update our text fields
1841 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1843 ctrl.Separator = cds.Separator;
1848 private void buttonPowerOn_Click(object sender, EventArgs e)
1853 private void buttonPowerOff_Click(object sender, EventArgs e)
1855 iDisplay.PowerOff();
1858 private void buttonShowClock_Click(object sender, EventArgs e)
1863 private void buttonHideClock_Click(object sender, EventArgs e)
1868 private void buttonUpdate_Click(object sender, EventArgs e)
1870 InstallUpdateSyncWithInfo();
1878 if (!iDisplay.IsOpen())
1883 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1884 iSkipFrameRendering = true;
1887 iDisplay.SwapBuffers();
1888 //Then show our clock
1889 iDisplay.ShowClock();
1897 if (!iDisplay.IsOpen())
1902 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1903 iSkipFrameRendering = false;
1904 iDisplay.HideClock();
1907 private void InstallUpdateSyncWithInfo()
1909 UpdateCheckInfo info = null;
1911 if (ApplicationDeployment.IsNetworkDeployed)
1913 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1917 info = ad.CheckForDetailedUpdate();
1920 catch (DeploymentDownloadException dde)
1922 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);
1925 catch (InvalidDeploymentException ide)
1927 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);
1930 catch (InvalidOperationException ioe)
1932 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1936 if (info.UpdateAvailable)
1938 Boolean doUpdate = true;
1940 if (!info.IsUpdateRequired)
1942 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1943 if (!(DialogResult.OK == dr))
1950 // Display a message that the app MUST reboot. Display the minimum required version.
1951 MessageBox.Show("This application has detected a mandatory update from your current " +
1952 "version to version " + info.MinimumRequiredVersion.ToString() +
1953 ". The application will now install the update and restart.",
1954 "Update Available", MessageBoxButtons.OK,
1955 MessageBoxIcon.Information);
1963 MessageBox.Show("The application has been upgraded, and will now restart.");
1964 Application.Restart();
1966 catch (DeploymentDownloadException dde)
1968 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1975 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1984 private void SysTrayHideShow()
1990 WindowState = FormWindowState.Normal;
1995 /// Use to handle minimize events.
1997 /// <param name="sender"></param>
1998 /// <param name="e"></param>
1999 private void MainForm_SizeChanged(object sender, EventArgs e)
2001 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2014 /// <param name="sender"></param>
2015 /// <param name="e"></param>
2016 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2018 //Our table layout size has changed which means our display size has changed.
2019 //We need to re-create our bitmap.
2020 iCreateBitmap = true;
2025 /// A UI thread copy of a client relevant data.
2026 /// Keeping this copy in the UI thread helps us deal with threading issues.
2028 public class ClientData
2030 public ClientData(string aSessionId, ICallback aCallback)
2032 SessionId = aSessionId;
2034 Fields = new List<DataField>();
2035 Layout = new TableLayout(1, 2); //Default to one column and two rows
2036 Callback = aCallback;
2039 public string SessionId { get; set; }
2040 public string Name { get; set; }
2041 public List<DataField> Fields { get; set; }
2042 public TableLayout Layout { get; set; }
2043 public ICallback Callback { get; set; }