Adding basic support for networks.
2 using System.Collections.Generic;
3 using System.ComponentModel;
8 using System.Threading.Tasks;
9 using System.Windows.Forms;
11 using CodeProject.Dialog;
12 using System.Drawing.Imaging;
13 using System.ServiceModel;
14 using System.Threading;
15 using System.Diagnostics;
16 using System.Deployment.Application;
17 using System.Reflection;
19 using NAudio.CoreAudioApi;
20 using NAudio.CoreAudioApi.Interfaces;
21 using System.Runtime.InteropServices;
25 using SharpDisplayClient;
28 namespace SharpDisplayManager
31 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
32 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
33 //Delegates are used for our thread safe method
34 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
35 public delegate void RemoveClientDelegate(string aSessionId);
36 public delegate void SetFieldDelegate(string SessionId, DataField aField);
37 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
38 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
39 public delegate void SetClientNameDelegate(string aSessionId, string aName);
40 public delegate void PlainUpdateDelegate();
44 /// Our Display manager main form
46 public partial class MainForm : Form, IMMNotificationClient
48 DateTime LastTickTime;
50 System.Drawing.Bitmap iBmp;
51 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
52 ServiceHost iServiceHost;
53 // Our collection of clients sorted by session id.
54 public Dictionary<string, ClientData> iClients;
55 // The name of the client which informations are currently displayed.
56 public string iCurrentClientSessionId;
57 ClientData iCurrentClientData;
60 //Function pointer for pixel color filtering
61 ColorProcessingDelegate iColorFx;
62 //Function pointer for pixel X coordinate intercept
63 CoordinateTranslationDelegate iScreenX;
64 //Function pointer for pixel Y coordinate intercept
65 CoordinateTranslationDelegate iScreenY;
67 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
68 private MMDevice iMultiMediaDevice;
70 private NetworkManager iNetworkManager;
74 /// Manage run when Windows startup option
76 private StartupManager iStartupManager;
81 private NotifyIconAdv iNotifyIcon;
85 iCurrentClientSessionId = "";
86 iCurrentClientData = null;
87 LastTickTime = DateTime.Now;
88 //Instantiate our display and register for events notifications
89 iDisplay = new Display();
90 iDisplay.OnOpened += OnDisplayOpened;
91 iDisplay.OnClosed += OnDisplayClosed;
93 iClients = new Dictionary<string, ClientData>();
94 iStartupManager = new StartupManager();
95 iNotifyIcon = new NotifyIconAdv();
97 //Have our designer initialize its controls
98 InitializeComponent();
100 //Populate device types
101 PopulateDeviceTypes();
103 //Initial status update
106 //We have a bug when drawing minimized and reusing our bitmap
107 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
108 iCreateBitmap = false;
110 //Minimize our window if desired
111 if (Properties.Settings.Default.StartMinimized)
113 WindowState = FormWindowState.Minimized;
121 /// <param name="sender"></param>
122 /// <param name="e"></param>
123 private void MainForm_Load(object sender, EventArgs e)
125 //Check if we are running a Click Once deployed application
126 if (ApplicationDeployment.IsNetworkDeployed)
128 //This is a proper Click Once installation, fetch and show our version number
129 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
133 //Not a proper Click Once installation, assuming development build then
134 this.Text += " - development";
138 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
139 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
140 UpdateAudioDeviceAndMasterVolumeThreadSafe();
143 iNetworkManager = new NetworkManager();
144 iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
145 UpdateNetworkStatus();
147 //Setup notification icon
150 // To make sure start up with minimize to tray works
151 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
157 //When not debugging we want the screen to be empty until a client takes over
160 //When developing we want at least one client for testing
161 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
164 //Open display connection on start-up if needed
165 if (Properties.Settings.Default.DisplayConnectOnStartup)
167 OpenDisplayConnection();
170 //Start our server so that we can get client requests
175 /// Called when our display is opened.
177 /// <param name="aDisplay"></param>
178 private void OnDisplayOpened(Display aDisplay)
180 //Set our screen size now that our display is connected
181 //Our panelDisplay is the container of our tableLayoutPanel
182 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
183 //panelDisplay needs an extra 2 pixels for borders on each sides
184 //tableLayoutPanel will eventually be the exact size of our display
185 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
186 panelDisplay.Size = size;
188 //Our display was just opened, update our UI
190 //Initiate asynchronous request
191 iDisplay.RequestFirmwareRevision();
194 UpdateMasterVolumeThreadSafe();
196 UpdateNetworkStatus();
199 //Testing icon in debug, no arm done if icon not supported
200 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
201 //iDisplay.SetAllIconsStatus(2);
207 /// Called when our display is closed.
209 /// <param name="aDisplay"></param>
210 private void OnDisplayClosed(Display aDisplay)
212 //Our display was just closed, update our UI consequently
216 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
218 //Update network status
219 UpdateNetworkStatus();
223 /// Update our Network Status
225 private void UpdateNetworkStatus()
227 if (iDisplay.IsOpen())
229 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetwork, iNetworkManager.NetworkListManager.IsConnectedToInternet);
235 /// Receive volume change notification and reflect changes on our slider.
237 /// <param name="data"></param>
238 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
240 UpdateMasterVolumeThreadSafe();
244 /// Update master volume when user moves our slider.
246 /// <param name="sender"></param>
247 /// <param name="e"></param>
248 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
250 //Just like Windows Volume Mixer we unmute if the volume is adjusted
251 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
252 //Set volume level according to our volume slider new position
253 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
258 /// Mute check box changed.
260 /// <param name="sender"></param>
261 /// <param name="e"></param>
262 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
264 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
268 /// Device State Changed
270 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
275 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
280 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
283 /// Default Device Changed
285 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
287 if (role == Role.Multimedia && flow == DataFlow.Render)
289 UpdateAudioDeviceAndMasterVolumeThreadSafe();
294 /// Property Value Changed
296 /// <param name="pwstrDeviceId"></param>
297 /// <param name="key"></param>
298 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
304 /// Update master volume indicators based our current system states.
305 /// This typically includes volume levels and mute status.
307 private void UpdateMasterVolumeThreadSafe()
309 if (this.InvokeRequired)
311 //Not in the proper thread, invoke ourselves
312 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
313 this.Invoke(d, new object[] { });
317 //Update volume slider
318 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
319 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
320 //Update mute checkbox
321 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
323 //If our display connection is open we need to update its icons
324 if (iDisplay.IsOpen())
326 //First take care our our volume level icons
327 int volumeIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume);
328 if (volumeIconCount > 0)
330 //Compute current volume level from system level and the number of segments in our display volume bar.
331 //That tells us how many segments in our volume bar needs to be turned on.
332 float currentVolume = volumeLevelScalar * volumeIconCount;
333 int segmentOnCount = Convert.ToInt32(currentVolume);
334 //Check if our segment count was rounded up, this will later be used for half brightness segment
335 bool roundedUp = segmentOnCount > currentVolume;
337 for (int i = 0; i < volumeIconCount; i++)
339 if (i < segmentOnCount)
341 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
342 if (i == segmentOnCount - 1 && roundedUp)
345 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, (iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1)/2);
350 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1);
355 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 0);
360 //Take care our our mute icon
361 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconMute, iMultiMediaDevice.AudioEndpointVolume.Mute);
369 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
371 if (this.InvokeRequired)
373 //Not in the proper thread, invoke ourselves
374 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
375 this.Invoke(d, new object[] { });
379 //We are in the correct thread just go ahead.
382 //Get our master volume
383 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
385 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
387 //Show our volume in our track bar
388 UpdateMasterVolumeThreadSafe();
390 //Register to get volume modifications
391 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
393 trackBarMasterVolume.Enabled = true;
397 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
398 Debug.WriteLine(ex.ToString());
399 //Something went wrong S/PDIF device ca throw exception I guess
400 trackBarMasterVolume.Enabled = false;
407 private void PopulateDeviceTypes()
409 int count = Display.TypeCount();
411 for (int i = 0; i < count; i++)
413 comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
420 private void SetupTrayIcon()
422 iNotifyIcon.Icon = GetIcon("vfd.ico");
423 iNotifyIcon.Text = "Sharp Display Manager";
424 iNotifyIcon.Visible = true;
426 //Double click toggles visibility - typically brings up the application
427 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
432 //Adding a context menu, useful to be able to exit the application
433 ContextMenu contextMenu = new ContextMenu();
434 //Context menu item to toggle visibility
435 MenuItem hideShowItem = new MenuItem("Hide/Show");
436 hideShowItem.Click += delegate(object obj, EventArgs args)
440 contextMenu.MenuItems.Add(hideShowItem);
442 //Context menu item separator
443 contextMenu.MenuItems.Add(new MenuItem("-"));
445 //Context menu exit item
446 MenuItem exitItem = new MenuItem("Exit");
447 exitItem.Click += delegate(object obj, EventArgs args)
451 contextMenu.MenuItems.Add(exitItem);
453 iNotifyIcon.ContextMenu = contextMenu;
457 /// Access icons from embedded resources.
459 /// <param name="name"></param>
460 /// <returns></returns>
461 public static Icon GetIcon(string name)
463 name = "SharpDisplayManager.Resources." + name;
466 Assembly.GetExecutingAssembly().GetManifestResourceNames();
467 for (int i = 0; i < names.Length; i++)
469 if (names[i].Replace('\\', '.') == name)
471 using (Stream stream = Assembly.GetExecutingAssembly().
472 GetManifestResourceStream(names[i]))
474 return new Icon(stream);
484 /// Set our current client.
485 /// This will take care of applying our client layout and set data fields.
487 /// <param name="aSessionId"></param>
488 void SetCurrentClient(string aSessionId)
490 if (aSessionId == iCurrentClientSessionId)
492 //Given client is already the current one.
493 //Don't bother changing anything then.
497 //Set current client ID.
498 iCurrentClientSessionId = aSessionId;
499 //Fetch and set current client data.
500 iCurrentClientData = iClients[aSessionId];
501 //Apply layout and set data fields.
502 UpdateTableLayoutPanel(iCurrentClientData);
505 private void buttonFont_Click(object sender, EventArgs e)
507 //fontDialog.ShowColor = true;
508 //fontDialog.ShowApply = true;
509 fontDialog.ShowEffects = true;
510 fontDialog.Font = cds.Font;
512 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
514 //fontDialog.ShowHelp = true;
516 //fontDlg.MaxSize = 40;
517 //fontDlg.MinSize = 22;
519 //fontDialog.Parent = this;
520 //fontDialog.StartPosition = FormStartPosition.CenterParent;
522 //DlgBox.ShowDialog(fontDialog);
524 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
525 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
527 //Set the fonts to all our labels in our layout
528 foreach (Control ctrl in tableLayoutPanel.Controls)
530 if (ctrl is MarqueeLabel)
532 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
537 cds.Font = fontDialog.Font;
538 Properties.Settings.Default.Save();
547 void CheckFontHeight()
549 //Show font height and width
550 labelFontHeight.Text = "Font height: " + cds.Font.Height;
551 float charWidth = IsFixedWidth(cds.Font);
552 if (charWidth == 0.0f)
554 labelFontWidth.Visible = false;
558 labelFontWidth.Visible = true;
559 labelFontWidth.Text = "Font width: " + charWidth;
562 MarqueeLabel label = null;
563 //Get the first label control we can find
564 foreach (Control ctrl in tableLayoutPanel.Controls)
566 if (ctrl is MarqueeLabel)
568 label = (MarqueeLabel)ctrl;
573 //Now check font height and show a warning if needed.
574 if (label != null && label.Font.Height > label.Height)
576 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
577 labelWarning.Visible = true;
581 labelWarning.Visible = false;
586 private void buttonCapture_Click(object sender, EventArgs e)
588 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
589 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
590 //Bitmap bmpToSave = new Bitmap(bmp);
591 bmp.Save("D:\\capture.png");
593 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
596 string outputFileName = "d:\\capture.png";
597 using (MemoryStream memory = new MemoryStream())
599 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
601 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
602 byte[] bytes = memory.ToArray();
603 fs.Write(bytes, 0, bytes.Length);
610 private void CheckForRequestResults()
612 if (iDisplay.IsRequestPending())
614 switch (iDisplay.AttemptRequestCompletion())
616 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
617 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
618 //Issue next request then
619 iDisplay.RequestPowerSupplyStatus();
622 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
623 if (iDisplay.PowerSupplyStatus())
625 toolStripStatusLabelPower.Text = "ON";
629 toolStripStatusLabelPower.Text = "OFF";
631 //Issue next request then
632 iDisplay.RequestDeviceId();
635 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
636 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
637 //No more request to issue
643 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
645 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
652 public static uint ColorUntouched(int aX, int aY, uint aPixel)
657 public static uint ColorInversed(int aX, int aY, uint aPixel)
662 public static uint ColorChessboard(int aX, int aY, uint aPixel)
664 if ((aX % 2 == 0) && (aY % 2 == 0))
668 else if ((aX % 2 != 0) && (aY % 2 != 0))
676 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
678 return aBmp.Width - aX - 1;
681 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
683 return iBmp.Height - aY - 1;
686 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
691 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
697 /// Select proper pixel delegates according to our current settings.
699 private void SetupPixelDelegates()
701 //Select our pixel processing routine
702 if (cds.InverseColors)
704 //iColorFx = ColorChessboard;
705 iColorFx = ColorInversed;
709 iColorFx = ColorWhiteIsOn;
712 //Select proper coordinate translation functions
713 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
714 if (cds.ReverseScreen)
716 iScreenX = ScreenReversedX;
717 iScreenY = ScreenReversedY;
727 //This is our timer tick responsible to perform our render
728 private void timer_Tick(object sender, EventArgs e)
730 //Update our animations
731 DateTime NewTickTime = DateTime.Now;
733 //Update animation for all our marquees
734 foreach (Control ctrl in tableLayoutPanel.Controls)
736 if (ctrl is MarqueeLabel)
738 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
744 if (iDisplay.IsOpen())
746 CheckForRequestResults();
751 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
753 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
754 //iBmp.Save("D:\\capture.png");
756 //Send it to our display
757 for (int i = 0; i < iBmp.Width; i++)
759 for (int j = 0; j < iBmp.Height; j++)
763 //Get our processed pixel coordinates
764 int x = iScreenX(iBmp, i);
765 int y = iScreenY(iBmp, j);
767 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
768 //Apply color effects
769 color = iColorFx(x,y,color);
771 iDisplay.SetPixel(x, y, color);
776 iDisplay.SwapBuffers();
780 //Compute instant FPS
781 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
783 LastTickTime = NewTickTime;
788 /// Attempt to establish connection with our display hardware.
790 private void OpenDisplayConnection()
792 CloseDisplayConnection();
794 if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
797 toolStripStatusLabelConnect.Text = "Connection error";
801 private void CloseDisplayConnection()
803 //Status will be updated upon receiving the closed event
807 private void buttonOpen_Click(object sender, EventArgs e)
809 OpenDisplayConnection();
812 private void buttonClose_Click(object sender, EventArgs e)
814 CloseDisplayConnection();
817 private void buttonClear_Click(object sender, EventArgs e)
820 iDisplay.SwapBuffers();
823 private void buttonFill_Click(object sender, EventArgs e)
826 iDisplay.SwapBuffers();
829 private void trackBarBrightness_Scroll(object sender, EventArgs e)
831 cds.Brightness = trackBarBrightness.Value;
832 Properties.Settings.Default.Save();
833 iDisplay.SetBrightness(trackBarBrightness.Value);
839 /// CDS stands for Current Display Settings
841 private DisplaySettings cds
845 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
846 if (settings == null)
848 settings = new DisplaysSettings();
850 Properties.Settings.Default.DisplaysSettings = settings;
853 //Make sure all our settings have been created
854 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
856 settings.Displays.Add(new DisplaySettings());
859 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
860 return displaySettings;
865 /// Check if the given font has a fixed character pitch.
867 /// <param name="ft"></param>
868 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
869 public float IsFixedWidth(Font ft)
871 Graphics g = CreateGraphics();
872 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
873 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
875 bool fixedWidth = true;
877 foreach (char c in charSizes)
878 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
890 /// Synchronize UI with settings
892 private void UpdateStatus()
895 checkBoxShowBorders.Checked = cds.ShowBorders;
896 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
898 //Set the proper font to each of our labels
899 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
901 ctrl.Font = cds.Font;
905 //Check if "run on Windows startup" is enabled
906 checkBoxAutoStart.Checked = iStartupManager.Startup;
908 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
909 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
910 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
911 checkBoxReverseScreen.Checked = cds.ReverseScreen;
912 checkBoxInverseColors.Checked = cds.InverseColors;
913 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
914 checkBoxScaleToFit.Checked = cds.ScaleToFit;
915 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
916 labelMinFontSize.Enabled = cds.ScaleToFit;
917 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
918 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
919 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
920 timer.Interval = cds.TimerInterval;
921 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
922 textBoxScrollLoopSeparator.Text = cds.Separator;
924 SetupPixelDelegates();
926 if (iDisplay.IsOpen())
928 //We have a display connection
929 //Reflect that in our UI
931 tableLayoutPanel.Enabled = true;
932 panelDisplay.Enabled = true;
934 //Only setup brightness if display is open
935 trackBarBrightness.Minimum = iDisplay.MinBrightness();
936 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
937 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
939 //Brightness out of range, this can occur when using auto-detect
940 //Use max brightness instead
941 trackBarBrightness.Value = iDisplay.MaxBrightness();
942 iDisplay.SetBrightness(iDisplay.MaxBrightness());
946 trackBarBrightness.Value = cds.Brightness;
947 iDisplay.SetBrightness(cds.Brightness);
950 //Try compute the steps to something that makes sense
951 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
952 trackBarBrightness.SmallChange = 1;
955 buttonFill.Enabled = true;
956 buttonClear.Enabled = true;
957 buttonOpen.Enabled = false;
958 buttonClose.Enabled = true;
959 trackBarBrightness.Enabled = true;
960 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
961 //+ " - " + iDisplay.SerialNumber();
963 if (iDisplay.SupportPowerOnOff())
965 buttonPowerOn.Enabled = true;
966 buttonPowerOff.Enabled = true;
970 buttonPowerOn.Enabled = false;
971 buttonPowerOff.Enabled = false;
974 if (iDisplay.SupportClock())
976 buttonShowClock.Enabled = true;
977 buttonHideClock.Enabled = true;
981 buttonShowClock.Enabled = false;
982 buttonHideClock.Enabled = false;
986 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
987 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel)>0;
989 if (cds.ShowVolumeLabel)
991 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
995 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1000 //Display is connection not available
1001 //Reflect that in our UI
1002 checkBoxShowVolumeLabel.Enabled = false;
1003 tableLayoutPanel.Enabled = false;
1004 panelDisplay.Enabled = false;
1005 buttonFill.Enabled = false;
1006 buttonClear.Enabled = false;
1007 buttonOpen.Enabled = true;
1008 buttonClose.Enabled = false;
1009 trackBarBrightness.Enabled = false;
1010 buttonPowerOn.Enabled = false;
1011 buttonPowerOff.Enabled = false;
1012 buttonShowClock.Enabled = false;
1013 buttonHideClock.Enabled = false;
1014 toolStripStatusLabelConnect.Text = "Disconnected";
1015 toolStripStatusLabelPower.Text = "N/A";
1024 /// <param name="sender"></param>
1025 /// <param name="e"></param>
1026 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1028 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1029 Properties.Settings.Default.Save();
1033 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1035 //Save our show borders setting
1036 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1037 cds.ShowBorders = checkBoxShowBorders.Checked;
1038 Properties.Settings.Default.Save();
1042 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1044 //Save our connect on startup setting
1045 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1046 Properties.Settings.Default.Save();
1049 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1051 //Save our "Minimize to tray" setting
1052 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1053 Properties.Settings.Default.Save();
1057 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1059 //Save our "Start minimized" setting
1060 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1061 Properties.Settings.Default.Save();
1064 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1066 iStartupManager.Startup = checkBoxAutoStart.Checked;
1070 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1072 //Save our reverse screen setting
1073 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1074 Properties.Settings.Default.Save();
1075 SetupPixelDelegates();
1078 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1080 //Save our inverse colors setting
1081 cds.InverseColors = checkBoxInverseColors.Checked;
1082 Properties.Settings.Default.Save();
1083 SetupPixelDelegates();
1086 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1088 //Save our scale to fit setting
1089 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1090 Properties.Settings.Default.Save();
1092 labelMinFontSize.Enabled = cds.ScaleToFit;
1093 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1096 private void MainForm_Resize(object sender, EventArgs e)
1098 if (WindowState == FormWindowState.Minimized)
1101 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1102 iCreateBitmap = true;
1106 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1108 iNetworkManager.Dispose();
1109 CloseDisplayConnection();
1111 e.Cancel = iClosing;
1114 public void StartServer()
1116 iServiceHost = new ServiceHost
1119 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1122 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1123 iServiceHost.Open();
1126 public void StopServer()
1128 if (iClients.Count > 0 && !iClosing)
1132 BroadcastCloseEvent();
1136 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1138 iClosing = false; //We make sure we force close if asked twice
1143 //We removed that as it often lags for some reason
1144 //iServiceHost.Close();
1148 public void BroadcastCloseEvent()
1150 Trace.TraceInformation("BroadcastCloseEvent - start");
1152 var inactiveClients = new List<string>();
1153 foreach (var client in iClients)
1155 //if (client.Key != eventData.ClientName)
1159 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1160 client.Value.Callback.OnCloseOrder(/*eventData*/);
1162 catch (Exception ex)
1164 inactiveClients.Add(client.Key);
1169 if (inactiveClients.Count > 0)
1171 foreach (var client in inactiveClients)
1173 iClients.Remove(client);
1174 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1178 if (iClients.Count==0)
1185 /// Just remove all our fields.
1187 private void ClearLayout()
1189 tableLayoutPanel.Controls.Clear();
1190 tableLayoutPanel.RowStyles.Clear();
1191 tableLayoutPanel.ColumnStyles.Clear();
1195 /// Just launch a demo client.
1197 private void StartNewClient(string aTopText = "", string aBottomText = "")
1199 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1200 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1201 clientThread.Start(myParams);
1205 private void buttonStartClient_Click(object sender, EventArgs e)
1210 private void buttonSuspend_Click(object sender, EventArgs e)
1212 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1213 timer.Enabled = !timer.Enabled;
1216 buttonSuspend.Text = "Run";
1220 buttonSuspend.Text = "Pause";
1224 private void buttonCloseClients_Click(object sender, EventArgs e)
1226 BroadcastCloseEvent();
1229 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1238 /// <param name="aSessionId"></param>
1239 /// <param name="aCallback"></param>
1240 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1242 if (this.InvokeRequired)
1244 //Not in the proper thread, invoke ourselves
1245 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1246 this.Invoke(d, new object[] { aSessionId, aCallback });
1250 //We are in the proper thread
1251 //Add this session to our collection of clients
1252 ClientData newClient = new ClientData(aSessionId, aCallback);
1253 Program.iMainForm.iClients.Add(aSessionId, newClient);
1254 //Add this session to our UI
1255 UpdateClientTreeViewNode(newClient);
1262 /// <param name="aSessionId"></param>
1263 public void RemoveClientThreadSafe(string aSessionId)
1265 if (this.InvokeRequired)
1267 //Not in the proper thread, invoke ourselves
1268 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1269 this.Invoke(d, new object[] { aSessionId });
1273 //We are in the proper thread
1274 //Remove this session from both client collection and UI tree view
1275 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1277 Program.iMainForm.iClients.Remove(aSessionId);
1278 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1281 if (iClients.Count == 0)
1283 //Clear our screen when last client disconnects
1288 //We were closing our form
1289 //All clients are now closed
1290 //Just resume our close operation
1301 /// <param name="aSessionId"></param>
1302 /// <param name="aLayout"></param>
1303 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1305 if (this.InvokeRequired)
1307 //Not in the proper thread, invoke ourselves
1308 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1309 this.Invoke(d, new object[] { aSessionId, aLayout });
1313 ClientData client = iClients[aSessionId];
1316 client.Layout = aLayout;
1317 UpdateTableLayoutPanel(client);
1319 UpdateClientTreeViewNode(client);
1327 /// <param name="aSessionId"></param>
1328 /// <param name="aField"></param>
1329 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1331 if (this.InvokeRequired)
1333 //Not in the proper thread, invoke ourselves
1334 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1335 this.Invoke(d, new object[] { aSessionId, aField });
1339 //We are in the proper thread
1340 //Call the non-thread-safe variant
1341 SetClientField(aSessionId, aField);
1348 /// <param name="aSessionId"></param>
1349 /// <param name="aField"></param>
1350 private void SetClientField(string aSessionId, DataField aField)
1352 SetCurrentClient(aSessionId);
1353 ClientData client = iClients[aSessionId];
1356 bool somethingChanged = false;
1358 //Make sure all our fields are in place
1359 while (client.Fields.Count < (aField.Index + 1))
1361 //Add a text field with proper index
1362 client.Fields.Add(new DataField(client.Fields.Count));
1363 somethingChanged = true;
1366 if (client.Fields[aField.Index].IsSameLayout(aField))
1368 //Same layout just update our field
1369 client.Fields[aField.Index] = aField;
1371 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1373 //Text field control already in place, just change the text
1374 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1375 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1376 label.Text = aField.Text;
1377 label.TextAlign = aField.Alignment;
1379 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1381 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1382 //Bitmap field control already in place just change the bitmap
1383 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1384 pictureBox.Image = aField.Bitmap;
1388 somethingChanged = true;
1389 //The requested control in our layout it not of the correct type
1390 //Wrong control type, re-create them all
1391 UpdateTableLayoutPanel(iCurrentClientData);
1396 somethingChanged = true;
1397 //Different layout, need to rebuild it
1398 client.Fields[aField.Index] = aField;
1399 UpdateTableLayoutPanel(iCurrentClientData);
1403 if (somethingChanged)
1405 UpdateClientTreeViewNode(client);
1413 /// <param name="aTexts"></param>
1414 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1416 if (this.InvokeRequired)
1418 //Not in the proper thread, invoke ourselves
1419 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1420 this.Invoke(d, new object[] { aSessionId, aFields });
1424 //Put each our text fields in a label control
1425 foreach (DataField field in aFields)
1427 SetClientField(aSessionId, field);
1435 /// <param name="aSessionId"></param>
1436 /// <param name="aName"></param>
1437 public void SetClientNameThreadSafe(string aSessionId, string aName)
1439 if (this.InvokeRequired)
1441 //Not in the proper thread, invoke ourselves
1442 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1443 this.Invoke(d, new object[] { aSessionId, aName });
1447 //We are in the proper thread
1449 ClientData client = iClients[aSessionId];
1453 client.Name = aName;
1454 //Update our tree-view
1455 UpdateClientTreeViewNode(client);
1463 /// <param name="aClient"></param>
1464 private void UpdateClientTreeViewNode(ClientData aClient)
1466 if (aClient == null)
1471 TreeNode node = null;
1472 //Check that our client node already exists
1473 //Get our client root node using its key which is our session ID
1474 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1475 if (nodes.Count()>0)
1477 //We already have a node for that client
1479 //Clear children as we are going to recreate them below
1484 //Client node does not exists create a new one
1485 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1486 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1492 if (aClient.Name != "")
1494 //We have a name, us it as text for our root node
1495 node.Text = aClient.Name;
1496 //Add a child with SessionId
1497 node.Nodes.Add(new TreeNode(aClient.SessionId));
1501 //No name, use session ID instead
1502 node.Text = aClient.SessionId;
1505 if (aClient.Fields.Count > 0)
1507 //Create root node for our texts
1508 TreeNode textsRoot = new TreeNode("Fields");
1509 node.Nodes.Add(textsRoot);
1510 //For each text add a new entry
1511 foreach (DataField field in aClient.Fields)
1513 if (!field.IsBitmap)
1515 DataField textField = (DataField)field;
1516 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1520 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1530 /// Update our table layout row styles to make sure each rows have similar height
1532 private void UpdateTableLayoutRowStyles()
1534 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1536 rowStyle.SizeType = SizeType.Percent;
1537 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1543 /// Empty and recreate our table layout with the given number of columns and rows.
1544 /// Sizes of rows and columns are uniform.
1546 /// <param name="aColumn"></param>
1547 /// <param name="aRow"></param>
1548 private void UpdateTableLayoutPanel(int aColumn, int aRow)
1550 tableLayoutPanel.Controls.Clear();
1551 tableLayoutPanel.RowStyles.Clear();
1552 tableLayoutPanel.ColumnStyles.Clear();
1553 tableLayoutPanel.RowCount = 0;
1554 tableLayoutPanel.ColumnCount = 0;
1556 while (tableLayoutPanel.RowCount < aRow)
1558 tableLayoutPanel.RowCount++;
1561 while (tableLayoutPanel.ColumnCount < aColumn)
1563 tableLayoutPanel.ColumnCount++;
1566 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1568 //Create our column styles
1569 this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.ColumnCount));
1571 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1575 //Create our row styles
1576 this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100 / tableLayoutPanel.RowCount));
1579 MarqueeLabel control = new SharpDisplayManager.MarqueeLabel();
1580 control.AutoEllipsis = true;
1581 control.AutoSize = true;
1582 control.BackColor = System.Drawing.Color.Transparent;
1583 control.Dock = System.Windows.Forms.DockStyle.Fill;
1584 control.Location = new System.Drawing.Point(1, 1);
1585 control.Margin = new System.Windows.Forms.Padding(0);
1586 control.Name = "marqueeLabelCol" + aColumn + "Row" + aRow;
1587 control.OwnTimer = false;
1588 control.PixelsPerSecond = 64;
1589 control.Separator = cds.Separator;
1590 control.MinFontSize = cds.MinFontSize;
1591 control.ScaleToFit = cds.ScaleToFit;
1592 //control.Size = new System.Drawing.Size(254, 30);
1593 //control.TabIndex = 2;
1594 control.Font = cds.Font;
1595 control.Text = "ABCDEFGHIJKLMNOPQRST-0123456789";
1596 control.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
1597 control.UseCompatibleTextRendering = true;
1599 tableLayoutPanel.Controls.Add(control, i, j);
1608 /// Update our display table layout.
1610 /// <param name="aLayout"></param>
1611 private void UpdateTableLayoutPanel(ClientData aClient)
1613 if (aClient == null)
1620 TableLayout layout = aClient.Layout;
1623 tableLayoutPanel.Controls.Clear();
1624 tableLayoutPanel.RowStyles.Clear();
1625 tableLayoutPanel.ColumnStyles.Clear();
1626 tableLayoutPanel.RowCount = 0;
1627 tableLayoutPanel.ColumnCount = 0;
1629 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1631 tableLayoutPanel.RowCount++;
1634 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1636 tableLayoutPanel.ColumnCount++;
1639 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1641 //Create our column styles
1642 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1644 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1648 //Create our row styles
1649 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1652 //Check if we already have a control
1653 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1654 if (existingControl!=null)
1656 //We already have a control in that cell as a results of row/col spanning
1657 //Move on to next cell then
1663 //Check if a client field already exists for that cell
1664 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1666 //No client field specified, create a text field by default
1667 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1670 //Create a control corresponding to the field specified for that cell
1671 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1672 Control control = CreateControlForDataField(field);
1674 //Add newly created control to our table layout at the specified row and column
1675 tableLayoutPanel.Controls.Add(control, i, j);
1676 //Make sure we specify row and column span for that new control
1677 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1678 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1683 while (aClient.Fields.Count > fieldCount)
1685 //We have too much fields for this layout
1686 //Just discard them until we get there
1687 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1694 /// Check our type of data field and create corresponding control
1696 /// <param name="aField"></param>
1697 private Control CreateControlForDataField(DataField aField)
1699 Control control=null;
1700 if (!aField.IsBitmap)
1702 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1703 label.AutoEllipsis = true;
1704 label.AutoSize = true;
1705 label.BackColor = System.Drawing.Color.Transparent;
1706 label.Dock = System.Windows.Forms.DockStyle.Fill;
1707 label.Location = new System.Drawing.Point(1, 1);
1708 label.Margin = new System.Windows.Forms.Padding(0);
1709 label.Name = "marqueeLabel" + aField.Index;
1710 label.OwnTimer = false;
1711 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1712 label.Separator = cds.Separator;
1713 label.MinFontSize = cds.MinFontSize;
1714 label.ScaleToFit = cds.ScaleToFit;
1715 //control.Size = new System.Drawing.Size(254, 30);
1716 //control.TabIndex = 2;
1717 label.Font = cds.Font;
1719 label.TextAlign = aField.Alignment;
1720 label.UseCompatibleTextRendering = true;
1721 label.Text = aField.Text;
1727 //Create picture box
1728 PictureBox picture = new PictureBox();
1729 picture.AutoSize = true;
1730 picture.BackColor = System.Drawing.Color.Transparent;
1731 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1732 picture.Location = new System.Drawing.Point(1, 1);
1733 picture.Margin = new System.Windows.Forms.Padding(0);
1734 picture.Name = "pictureBox" + aField;
1736 picture.Image = aField.Bitmap;
1745 /// Called when the user selected a new display type.
1747 /// <param name="sender"></param>
1748 /// <param name="e"></param>
1749 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1751 //Store the selected display type in our settings
1752 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1753 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1754 Properties.Settings.Default.Save();
1756 //Try re-opening the display connection if we were already connected.
1757 //Otherwise just update our status to reflect display type change.
1758 if (iDisplay.IsOpen())
1760 OpenDisplayConnection();
1768 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1770 if (maskedTextBoxTimerInterval.Text != "")
1772 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1776 timer.Interval = interval;
1777 cds.TimerInterval = timer.Interval;
1778 Properties.Settings.Default.Save();
1783 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1785 if (maskedTextBoxMinFontSize.Text != "")
1787 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1789 if (minFontSize > 0)
1791 cds.MinFontSize = minFontSize;
1792 Properties.Settings.Default.Save();
1793 //We need to recreate our layout for that change to take effect
1794 UpdateTableLayoutPanel(iCurrentClientData);
1800 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1802 if (maskedTextBoxScrollingSpeed.Text != "")
1804 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1806 if (scrollingSpeed > 0)
1808 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1809 Properties.Settings.Default.Save();
1810 //We need to recreate our layout for that change to take effect
1811 UpdateTableLayoutPanel(iCurrentClientData);
1816 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1818 cds.Separator = textBoxScrollLoopSeparator.Text;
1819 Properties.Settings.Default.Save();
1821 //Update our text fields
1822 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1824 ctrl.Separator = cds.Separator;
1829 private void buttonPowerOn_Click(object sender, EventArgs e)
1834 private void buttonPowerOff_Click(object sender, EventArgs e)
1836 iDisplay.PowerOff();
1839 private void buttonShowClock_Click(object sender, EventArgs e)
1841 iDisplay.ShowClock();
1844 private void buttonHideClock_Click(object sender, EventArgs e)
1846 iDisplay.HideClock();
1849 private void buttonUpdate_Click(object sender, EventArgs e)
1851 InstallUpdateSyncWithInfo();
1855 private void InstallUpdateSyncWithInfo()
1857 UpdateCheckInfo info = null;
1859 if (ApplicationDeployment.IsNetworkDeployed)
1861 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1865 info = ad.CheckForDetailedUpdate();
1868 catch (DeploymentDownloadException dde)
1870 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);
1873 catch (InvalidDeploymentException ide)
1875 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);
1878 catch (InvalidOperationException ioe)
1880 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1884 if (info.UpdateAvailable)
1886 Boolean doUpdate = true;
1888 if (!info.IsUpdateRequired)
1890 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1891 if (!(DialogResult.OK == dr))
1898 // Display a message that the app MUST reboot. Display the minimum required version.
1899 MessageBox.Show("This application has detected a mandatory update from your current " +
1900 "version to version " + info.MinimumRequiredVersion.ToString() +
1901 ". The application will now install the update and restart.",
1902 "Update Available", MessageBoxButtons.OK,
1903 MessageBoxIcon.Information);
1911 MessageBox.Show("The application has been upgraded, and will now restart.");
1912 Application.Restart();
1914 catch (DeploymentDownloadException dde)
1916 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1923 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1932 private void SysTrayHideShow()
1938 WindowState = FormWindowState.Normal;
1943 /// Use to handle minimize events.
1945 /// <param name="sender"></param>
1946 /// <param name="e"></param>
1947 private void MainForm_SizeChanged(object sender, EventArgs e)
1949 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
1962 /// <param name="sender"></param>
1963 /// <param name="e"></param>
1964 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
1966 //Our table layout size has changed which means our display size has changed.
1967 //We need to re-create our bitmap.
1968 iCreateBitmap = true;
1973 /// A UI thread copy of a client relevant data.
1974 /// Keeping this copy in the UI thread helps us deal with threading issues.
1976 public class ClientData
1978 public ClientData(string aSessionId, ICallback aCallback)
1980 SessionId = aSessionId;
1982 Fields = new List<DataField>();
1983 Layout = new TableLayout(1, 2); //Default to one column and two rows
1984 Callback = aCallback;
1987 public string SessionId { get; set; }
1988 public string Name { get; set; }
1989 public List<DataField> Fields { get; set; }
1990 public TableLayout Layout { get; set; }
1991 public ICallback Callback { get; set; }