Adding dependency on SharpLibHid.
Adding hard coded handling of green start key to launch MediaPortal.
2 // Copyright (C) 2014-2015 Stéphane Lenclud.
4 // This file is part of SharpDisplayManager.
6 // SharpDisplayManager is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // SharpDisplayManager is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with SharpDisplayManager. If not, see <http://www.gnu.org/licenses/>.
21 using System.Collections.Generic;
22 using System.ComponentModel;
27 using System.Threading.Tasks;
28 using System.Windows.Forms;
30 using CodeProject.Dialog;
31 using System.Drawing.Imaging;
32 using System.ServiceModel;
33 using System.Threading;
34 using System.Diagnostics;
35 using System.Deployment.Application;
36 using System.Reflection;
38 using NAudio.CoreAudioApi;
39 using NAudio.CoreAudioApi.Interfaces;
40 using System.Runtime.InteropServices;
44 using SharpDisplayClient;
48 namespace SharpDisplayManager
51 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
52 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
53 //Delegates are used for our thread safe method
54 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
55 public delegate void RemoveClientDelegate(string aSessionId);
56 public delegate void SetFieldDelegate(string SessionId, DataField aField);
57 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
58 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
59 public delegate void SetClientNameDelegate(string aSessionId, string aName);
60 public delegate void PlainUpdateDelegate();
64 /// Our Display manager main form
66 public partial class MainForm : Form, IMMNotificationClient
68 DateTime LastTickTime;
70 System.Drawing.Bitmap iBmp;
71 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
72 ServiceHost iServiceHost;
73 // Our collection of clients sorted by session id.
74 public Dictionary<string, ClientData> iClients;
75 // The name of the client which informations are currently displayed.
76 public string iCurrentClientSessionId;
77 ClientData iCurrentClientData;
81 public bool iSkipFrameRendering;
82 //Function pointer for pixel color filtering
83 ColorProcessingDelegate iColorFx;
84 //Function pointer for pixel X coordinate intercept
85 CoordinateTranslationDelegate iScreenX;
86 //Function pointer for pixel Y coordinate intercept
87 CoordinateTranslationDelegate iScreenY;
89 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
90 private MMDevice iMultiMediaDevice;
92 private NetworkManager iNetworkManager;
96 /// Manage run when Windows startup option
98 private StartupManager iStartupManager;
101 /// System tray icon.
103 private NotifyIconAdv iNotifyIcon;
107 iSkipFrameRendering = false;
109 iCurrentClientSessionId = "";
110 iCurrentClientData = null;
111 LastTickTime = DateTime.Now;
112 //Instantiate our display and register for events notifications
113 iDisplay = new Display();
114 iDisplay.OnOpened += OnDisplayOpened;
115 iDisplay.OnClosed += OnDisplayClosed;
117 iClients = new Dictionary<string, ClientData>();
118 iStartupManager = new StartupManager();
119 iNotifyIcon = new NotifyIconAdv();
121 //Have our designer initialize its controls
122 InitializeComponent();
124 //Populate device types
125 PopulateDeviceTypes();
127 //Initial status update
130 //We have a bug when drawing minimized and reusing our bitmap
131 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
132 iCreateBitmap = false;
134 //Minimize our window if desired
135 if (Properties.Settings.Default.StartMinimized)
137 WindowState = FormWindowState.Minimized;
145 /// <param name="sender"></param>
146 /// <param name="e"></param>
147 private void MainForm_Load(object sender, EventArgs e)
149 //Check if we are running a Click Once deployed application
150 if (ApplicationDeployment.IsNetworkDeployed)
152 //This is a proper Click Once installation, fetch and show our version number
153 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
157 //Not a proper Click Once installation, assuming development build then
158 this.Text += " - development";
162 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
163 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
164 UpdateAudioDeviceAndMasterVolumeThreadSafe();
167 iNetworkManager = new NetworkManager();
168 iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
169 UpdateNetworkStatus();
171 //Setup notification icon
174 // To make sure start up with minimize to tray works
175 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
181 //When not debugging we want the screen to be empty until a client takes over
184 //When developing we want at least one client for testing
185 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
188 //Open display connection on start-up if needed
189 if (Properties.Settings.Default.DisplayConnectOnStartup)
191 OpenDisplayConnection();
194 //Start our server so that we can get client requests
197 //Register for HID events
198 RegisterHidDevices();
202 /// Called when our display is opened.
204 /// <param name="aDisplay"></param>
205 private void OnDisplayOpened(Display aDisplay)
207 //Make sure we resume frame rendering
208 iSkipFrameRendering = false;
210 //Set our screen size now that our display is connected
211 //Our panelDisplay is the container of our tableLayoutPanel
212 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
213 //panelDisplay needs an extra 2 pixels for borders on each sides
214 //tableLayoutPanel will eventually be the exact size of our display
215 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
216 panelDisplay.Size = size;
218 //Our display was just opened, update our UI
220 //Initiate asynchronous request
221 iDisplay.RequestFirmwareRevision();
224 UpdateMasterVolumeThreadSafe();
226 UpdateNetworkStatus();
229 //Testing icon in debug, no arm done if icon not supported
230 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
231 //iDisplay.SetAllIconsStatus(2);
237 /// Called when our display is closed.
239 /// <param name="aDisplay"></param>
240 private void OnDisplayClosed(Display aDisplay)
242 //Our display was just closed, update our UI consequently
246 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
248 //Update network status
249 UpdateNetworkStatus();
253 /// Update our Network Status
255 private void UpdateNetworkStatus()
257 if (iDisplay.IsOpen())
259 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconInternet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
260 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
265 int iLastNetworkIconIndex = 0;
266 int iUpdateCountSinceLastNetworkAnimation = 0;
271 private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
273 iUpdateCountSinceLastNetworkAnimation++;
274 iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
276 if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
278 int iconCount=iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal);
281 //Prevents div by zero and other undefined behavior
284 iLastNetworkIconIndex++;
285 iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2);
286 for (int i=0;i<iconCount;i++)
288 if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && !(i == 1 && iLastNetworkIconIndex > 4))
290 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
294 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconNetworkSignal,i);
303 /// Receive volume change notification and reflect changes on our slider.
305 /// <param name="data"></param>
306 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
308 UpdateMasterVolumeThreadSafe();
312 /// Update master volume when user moves our slider.
314 /// <param name="sender"></param>
315 /// <param name="e"></param>
316 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
318 //Just like Windows Volume Mixer we unmute if the volume is adjusted
319 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
320 //Set volume level according to our volume slider new position
321 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
326 /// Mute check box changed.
328 /// <param name="sender"></param>
329 /// <param name="e"></param>
330 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
332 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
336 /// Device State Changed
338 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
343 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
348 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
351 /// Default Device Changed
353 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
355 if (role == Role.Multimedia && flow == DataFlow.Render)
357 UpdateAudioDeviceAndMasterVolumeThreadSafe();
362 /// Property Value Changed
364 /// <param name="pwstrDeviceId"></param>
365 /// <param name="key"></param>
366 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
372 /// Update master volume indicators based our current system states.
373 /// This typically includes volume levels and mute status.
375 private void UpdateMasterVolumeThreadSafe()
377 if (this.InvokeRequired)
379 //Not in the proper thread, invoke ourselves
380 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
381 this.Invoke(d, new object[] { });
385 //Update volume slider
386 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
387 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
388 //Update mute checkbox
389 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
391 //If our display connection is open we need to update its icons
392 if (iDisplay.IsOpen())
394 //First take care our our volume level icons
395 int volumeIconCount = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume);
396 if (volumeIconCount > 0)
398 //Compute current volume level from system level and the number of segments in our display volume bar.
399 //That tells us how many segments in our volume bar needs to be turned on.
400 float currentVolume = volumeLevelScalar * volumeIconCount;
401 int segmentOnCount = Convert.ToInt32(currentVolume);
402 //Check if our segment count was rounded up, this will later be used for half brightness segment
403 bool roundedUp = segmentOnCount > currentVolume;
405 for (int i = 0; i < volumeIconCount; i++)
407 if (i < segmentOnCount)
409 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
410 if (i == segmentOnCount - 1 && roundedUp)
413 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, (iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1)/2);
418 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, iDisplay.IconStatusCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolume) - 1);
423 iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconVolume, i, 0);
428 //Take care our our mute icon
429 iDisplay.SetIconOnOff(Display.TMiniDisplayIconType.EMiniDisplayIconMute, iMultiMediaDevice.AudioEndpointVolume.Mute);
437 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
439 if (this.InvokeRequired)
441 //Not in the proper thread, invoke ourselves
442 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
443 this.Invoke(d, new object[] { });
447 //We are in the correct thread just go ahead.
450 //Get our master volume
451 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
453 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
455 //Show our volume in our track bar
456 UpdateMasterVolumeThreadSafe();
458 //Register to get volume modifications
459 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
461 trackBarMasterVolume.Enabled = true;
465 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
466 Debug.WriteLine(ex.ToString());
467 //Something went wrong S/PDIF device ca throw exception I guess
468 trackBarMasterVolume.Enabled = false;
475 private void PopulateDeviceTypes()
477 int count = Display.TypeCount();
479 for (int i = 0; i < count; i++)
481 comboBoxDisplayType.Items.Add(Display.TypeName((Display.TMiniDisplayType)i));
488 private void SetupTrayIcon()
490 iNotifyIcon.Icon = GetIcon("vfd.ico");
491 iNotifyIcon.Text = "Sharp Display Manager";
492 iNotifyIcon.Visible = true;
494 //Double click toggles visibility - typically brings up the application
495 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
500 //Adding a context menu, useful to be able to exit the application
501 ContextMenu contextMenu = new ContextMenu();
502 //Context menu item to toggle visibility
503 MenuItem hideShowItem = new MenuItem("Hide/Show");
504 hideShowItem.Click += delegate(object obj, EventArgs args)
508 contextMenu.MenuItems.Add(hideShowItem);
510 //Context menu item separator
511 contextMenu.MenuItems.Add(new MenuItem("-"));
513 //Context menu exit item
514 MenuItem exitItem = new MenuItem("Exit");
515 exitItem.Click += delegate(object obj, EventArgs args)
519 contextMenu.MenuItems.Add(exitItem);
521 iNotifyIcon.ContextMenu = contextMenu;
525 /// Access icons from embedded resources.
527 /// <param name="name"></param>
528 /// <returns></returns>
529 public static Icon GetIcon(string name)
531 name = "SharpDisplayManager.Resources." + name;
534 Assembly.GetExecutingAssembly().GetManifestResourceNames();
535 for (int i = 0; i < names.Length; i++)
537 if (names[i].Replace('\\', '.') == name)
539 using (Stream stream = Assembly.GetExecutingAssembly().
540 GetManifestResourceStream(names[i]))
542 return new Icon(stream);
552 /// Set our current client.
553 /// This will take care of applying our client layout and set data fields.
555 /// <param name="aSessionId"></param>
556 void SetCurrentClient(string aSessionId)
558 if (aSessionId == iCurrentClientSessionId)
560 //Given client is already the current one.
561 //Don't bother changing anything then.
565 //Set current client ID.
566 iCurrentClientSessionId = aSessionId;
567 //Fetch and set current client data.
568 iCurrentClientData = iClients[aSessionId];
569 //Apply layout and set data fields.
570 UpdateTableLayoutPanel(iCurrentClientData);
573 private void buttonFont_Click(object sender, EventArgs e)
575 //fontDialog.ShowColor = true;
576 //fontDialog.ShowApply = true;
577 fontDialog.ShowEffects = true;
578 fontDialog.Font = cds.Font;
580 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
582 //fontDialog.ShowHelp = true;
584 //fontDlg.MaxSize = 40;
585 //fontDlg.MinSize = 22;
587 //fontDialog.Parent = this;
588 //fontDialog.StartPosition = FormStartPosition.CenterParent;
590 //DlgBox.ShowDialog(fontDialog);
592 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
593 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
595 //Set the fonts to all our labels in our layout
596 foreach (Control ctrl in tableLayoutPanel.Controls)
598 if (ctrl is MarqueeLabel)
600 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
605 cds.Font = fontDialog.Font;
606 Properties.Settings.Default.Save();
615 void CheckFontHeight()
617 //Show font height and width
618 labelFontHeight.Text = "Font height: " + cds.Font.Height;
619 float charWidth = IsFixedWidth(cds.Font);
620 if (charWidth == 0.0f)
622 labelFontWidth.Visible = false;
626 labelFontWidth.Visible = true;
627 labelFontWidth.Text = "Font width: " + charWidth;
630 MarqueeLabel label = null;
631 //Get the first label control we can find
632 foreach (Control ctrl in tableLayoutPanel.Controls)
634 if (ctrl is MarqueeLabel)
636 label = (MarqueeLabel)ctrl;
641 //Now check font height and show a warning if needed.
642 if (label != null && label.Font.Height > label.Height)
644 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
645 labelWarning.Visible = true;
649 labelWarning.Visible = false;
654 private void buttonCapture_Click(object sender, EventArgs e)
656 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
657 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
658 //Bitmap bmpToSave = new Bitmap(bmp);
659 bmp.Save("D:\\capture.png");
661 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
664 string outputFileName = "d:\\capture.png";
665 using (MemoryStream memory = new MemoryStream())
667 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
669 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
670 byte[] bytes = memory.ToArray();
671 fs.Write(bytes, 0, bytes.Length);
678 private void CheckForRequestResults()
680 if (iDisplay.IsRequestPending())
682 switch (iDisplay.AttemptRequestCompletion())
684 case Display.TMiniDisplayRequest.EMiniDisplayRequestFirmwareRevision:
685 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
686 //Issue next request then
687 iDisplay.RequestPowerSupplyStatus();
690 case Display.TMiniDisplayRequest.EMiniDisplayRequestPowerSupplyStatus:
691 if (iDisplay.PowerSupplyStatus())
693 toolStripStatusLabelPower.Text = "ON";
697 toolStripStatusLabelPower.Text = "OFF";
699 //Issue next request then
700 iDisplay.RequestDeviceId();
703 case Display.TMiniDisplayRequest.EMiniDisplayRequestDeviceId:
704 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
705 //No more request to issue
711 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
713 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
720 public static uint ColorUntouched(int aX, int aY, uint aPixel)
725 public static uint ColorInversed(int aX, int aY, uint aPixel)
730 public static uint ColorChessboard(int aX, int aY, uint aPixel)
732 if ((aX % 2 == 0) && (aY % 2 == 0))
736 else if ((aX % 2 != 0) && (aY % 2 != 0))
744 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
746 return aBmp.Width - aX - 1;
749 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
751 return iBmp.Height - aY - 1;
754 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
759 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
765 /// Select proper pixel delegates according to our current settings.
767 private void SetupPixelDelegates()
769 //Select our pixel processing routine
770 if (cds.InverseColors)
772 //iColorFx = ColorChessboard;
773 iColorFx = ColorInversed;
777 iColorFx = ColorWhiteIsOn;
780 //Select proper coordinate translation functions
781 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
782 if (cds.ReverseScreen)
784 iScreenX = ScreenReversedX;
785 iScreenY = ScreenReversedY;
795 //This is our timer tick responsible to perform our render
796 private void timer_Tick(object sender, EventArgs e)
798 //Update our animations
799 DateTime NewTickTime = DateTime.Now;
801 UpdateNetworkSignal(LastTickTime, NewTickTime);
803 //Update animation for all our marquees
804 foreach (Control ctrl in tableLayoutPanel.Controls)
806 if (ctrl is MarqueeLabel)
808 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
813 if (iDisplay.IsOpen())
815 CheckForRequestResults();
817 //Check if frame rendering is needed
818 //Typically used when showing clock
819 if (!iSkipFrameRendering)
824 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
826 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
827 //iBmp.Save("D:\\capture.png");
829 //Send it to our display
830 for (int i = 0; i < iBmp.Width; i++)
832 for (int j = 0; j < iBmp.Height; j++)
836 //Get our processed pixel coordinates
837 int x = iScreenX(iBmp, i);
838 int y = iScreenY(iBmp, j);
840 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
841 //Apply color effects
842 color = iColorFx(x, y, color);
844 iDisplay.SetPixel(x, y, color);
849 iDisplay.SwapBuffers();
853 //Compute instant FPS
854 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
856 LastTickTime = NewTickTime;
861 /// Attempt to establish connection with our display hardware.
863 private void OpenDisplayConnection()
865 CloseDisplayConnection();
867 if (!iDisplay.Open((Display.TMiniDisplayType)cds.DisplayType))
870 toolStripStatusLabelConnect.Text = "Connection error";
874 private void CloseDisplayConnection()
876 //Status will be updated upon receiving the closed event
878 if (iDisplay == null || !iDisplay.IsOpen())
883 //Do not clear if we gave up on rendering already.
884 //This means we will keep on displaying clock on MDM166AA for instance.
885 if (!iSkipFrameRendering)
888 iDisplay.SwapBuffers();
891 iDisplay.SetAllIconsStatus(0); //Turn off all icons
895 private void buttonOpen_Click(object sender, EventArgs e)
897 OpenDisplayConnection();
900 private void buttonClose_Click(object sender, EventArgs e)
902 CloseDisplayConnection();
905 private void buttonClear_Click(object sender, EventArgs e)
908 iDisplay.SwapBuffers();
911 private void buttonFill_Click(object sender, EventArgs e)
914 iDisplay.SwapBuffers();
917 private void trackBarBrightness_Scroll(object sender, EventArgs e)
919 cds.Brightness = trackBarBrightness.Value;
920 Properties.Settings.Default.Save();
921 iDisplay.SetBrightness(trackBarBrightness.Value);
927 /// CDS stands for Current Display Settings
929 private DisplaySettings cds
933 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
934 if (settings == null)
936 settings = new DisplaysSettings();
938 Properties.Settings.Default.DisplaysSettings = settings;
941 //Make sure all our settings have been created
942 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
944 settings.Displays.Add(new DisplaySettings());
947 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
948 return displaySettings;
953 /// Check if the given font has a fixed character pitch.
955 /// <param name="ft"></param>
956 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
957 public float IsFixedWidth(Font ft)
959 Graphics g = CreateGraphics();
960 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
961 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
963 bool fixedWidth = true;
965 foreach (char c in charSizes)
966 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
978 /// Synchronize UI with settings
980 private void UpdateStatus()
983 checkBoxShowBorders.Checked = cds.ShowBorders;
984 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
986 //Set the proper font to each of our labels
987 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
989 ctrl.Font = cds.Font;
993 //Check if "run on Windows startup" is enabled
994 checkBoxAutoStart.Checked = iStartupManager.Startup;
996 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
997 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
998 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
999 checkBoxReverseScreen.Checked = cds.ReverseScreen;
1000 checkBoxInverseColors.Checked = cds.InverseColors;
1001 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
1002 checkBoxScaleToFit.Checked = cds.ScaleToFit;
1003 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1004 labelMinFontSize.Enabled = cds.ScaleToFit;
1005 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
1006 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
1007 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
1008 timer.Interval = cds.TimerInterval;
1009 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
1010 textBoxScrollLoopSeparator.Text = cds.Separator;
1012 SetupPixelDelegates();
1014 if (iDisplay.IsOpen())
1016 //We have a display connection
1017 //Reflect that in our UI
1019 tableLayoutPanel.Enabled = true;
1020 panelDisplay.Enabled = true;
1022 //Only setup brightness if display is open
1023 trackBarBrightness.Minimum = iDisplay.MinBrightness();
1024 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
1025 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
1027 //Brightness out of range, this can occur when using auto-detect
1028 //Use max brightness instead
1029 trackBarBrightness.Value = iDisplay.MaxBrightness();
1030 iDisplay.SetBrightness(iDisplay.MaxBrightness());
1034 trackBarBrightness.Value = cds.Brightness;
1035 iDisplay.SetBrightness(cds.Brightness);
1038 //Try compute the steps to something that makes sense
1039 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
1040 trackBarBrightness.SmallChange = 1;
1043 buttonFill.Enabled = true;
1044 buttonClear.Enabled = true;
1045 buttonOpen.Enabled = false;
1046 buttonClose.Enabled = true;
1047 trackBarBrightness.Enabled = true;
1048 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1049 //+ " - " + iDisplay.SerialNumber();
1051 if (iDisplay.SupportPowerOnOff())
1053 buttonPowerOn.Enabled = true;
1054 buttonPowerOff.Enabled = true;
1058 buttonPowerOn.Enabled = false;
1059 buttonPowerOff.Enabled = false;
1062 if (iDisplay.SupportClock())
1064 buttonShowClock.Enabled = true;
1065 buttonHideClock.Enabled = true;
1069 buttonShowClock.Enabled = false;
1070 buttonHideClock.Enabled = false;
1074 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1075 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel)>0;
1077 if (cds.ShowVolumeLabel)
1079 iDisplay.SetIconOn(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1083 iDisplay.SetIconOff(Display.TMiniDisplayIconType.EMiniDisplayIconVolumeLabel);
1088 //Display is connection not available
1089 //Reflect that in our UI
1090 checkBoxShowVolumeLabel.Enabled = false;
1091 tableLayoutPanel.Enabled = false;
1092 panelDisplay.Enabled = false;
1093 buttonFill.Enabled = false;
1094 buttonClear.Enabled = false;
1095 buttonOpen.Enabled = true;
1096 buttonClose.Enabled = false;
1097 trackBarBrightness.Enabled = false;
1098 buttonPowerOn.Enabled = false;
1099 buttonPowerOff.Enabled = false;
1100 buttonShowClock.Enabled = false;
1101 buttonHideClock.Enabled = false;
1102 toolStripStatusLabelConnect.Text = "Disconnected";
1103 toolStripStatusLabelPower.Text = "N/A";
1112 /// <param name="sender"></param>
1113 /// <param name="e"></param>
1114 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1116 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1117 Properties.Settings.Default.Save();
1121 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1123 //Save our show borders setting
1124 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1125 cds.ShowBorders = checkBoxShowBorders.Checked;
1126 Properties.Settings.Default.Save();
1130 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1132 //Save our connect on startup setting
1133 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1134 Properties.Settings.Default.Save();
1137 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1139 //Save our "Minimize to tray" setting
1140 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1141 Properties.Settings.Default.Save();
1145 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1147 //Save our "Start minimized" setting
1148 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1149 Properties.Settings.Default.Save();
1152 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1154 iStartupManager.Startup = checkBoxAutoStart.Checked;
1158 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1160 //Save our reverse screen setting
1161 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1162 Properties.Settings.Default.Save();
1163 SetupPixelDelegates();
1166 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1168 //Save our inverse colors setting
1169 cds.InverseColors = checkBoxInverseColors.Checked;
1170 Properties.Settings.Default.Save();
1171 SetupPixelDelegates();
1174 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1176 //Save our scale to fit setting
1177 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1178 Properties.Settings.Default.Save();
1180 labelMinFontSize.Enabled = cds.ScaleToFit;
1181 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1184 private void MainForm_Resize(object sender, EventArgs e)
1186 if (WindowState == FormWindowState.Minimized)
1189 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1190 iCreateBitmap = true;
1194 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1196 iNetworkManager.Dispose();
1197 CloseDisplayConnection();
1199 e.Cancel = iClosing;
1202 public void StartServer()
1204 iServiceHost = new ServiceHost
1207 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1210 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1211 iServiceHost.Open();
1214 public void StopServer()
1216 if (iClients.Count > 0 && !iClosing)
1220 BroadcastCloseEvent();
1224 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1226 iClosing = false; //We make sure we force close if asked twice
1231 //We removed that as it often lags for some reason
1232 //iServiceHost.Close();
1236 public void BroadcastCloseEvent()
1238 Trace.TraceInformation("BroadcastCloseEvent - start");
1240 var inactiveClients = new List<string>();
1241 foreach (var client in iClients)
1243 //if (client.Key != eventData.ClientName)
1247 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1248 client.Value.Callback.OnCloseOrder(/*eventData*/);
1250 catch (Exception ex)
1252 inactiveClients.Add(client.Key);
1257 if (inactiveClients.Count > 0)
1259 foreach (var client in inactiveClients)
1261 iClients.Remove(client);
1262 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1266 if (iClients.Count==0)
1273 /// Just remove all our fields.
1275 private void ClearLayout()
1277 tableLayoutPanel.Controls.Clear();
1278 tableLayoutPanel.RowStyles.Clear();
1279 tableLayoutPanel.ColumnStyles.Clear();
1280 iCurrentClientData = null;
1284 /// Just launch a demo client.
1286 private void StartNewClient(string aTopText = "", string aBottomText = "")
1288 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1289 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1290 clientThread.Start(myParams);
1294 private void buttonStartClient_Click(object sender, EventArgs e)
1299 private void buttonSuspend_Click(object sender, EventArgs e)
1301 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1302 timer.Enabled = !timer.Enabled;
1305 buttonSuspend.Text = "Run";
1309 buttonSuspend.Text = "Pause";
1313 private void buttonCloseClients_Click(object sender, EventArgs e)
1315 BroadcastCloseEvent();
1318 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1327 /// <param name="aSessionId"></param>
1328 /// <param name="aCallback"></param>
1329 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1331 if (this.InvokeRequired)
1333 //Not in the proper thread, invoke ourselves
1334 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1335 this.Invoke(d, new object[] { aSessionId, aCallback });
1339 //We are in the proper thread
1340 //Add this session to our collection of clients
1341 ClientData newClient = new ClientData(aSessionId, aCallback);
1342 Program.iMainForm.iClients.Add(aSessionId, newClient);
1343 //Add this session to our UI
1344 UpdateClientTreeViewNode(newClient);
1351 /// <param name="aSessionId"></param>
1352 public void RemoveClientThreadSafe(string aSessionId)
1354 if (this.InvokeRequired)
1356 //Not in the proper thread, invoke ourselves
1357 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1358 this.Invoke(d, new object[] { aSessionId });
1362 //We are in the proper thread
1363 //Remove this session from both client collection and UI tree view
1364 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1366 Program.iMainForm.iClients.Remove(aSessionId);
1367 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1370 if (iClients.Count == 0)
1372 //Clear our screen when last client disconnects
1377 //We were closing our form
1378 //All clients are now closed
1379 //Just resume our close operation
1390 /// <param name="aSessionId"></param>
1391 /// <param name="aLayout"></param>
1392 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1394 if (this.InvokeRequired)
1396 //Not in the proper thread, invoke ourselves
1397 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1398 this.Invoke(d, new object[] { aSessionId, aLayout });
1402 ClientData client = iClients[aSessionId];
1405 client.Layout = aLayout;
1406 UpdateTableLayoutPanel(client);
1408 UpdateClientTreeViewNode(client);
1416 /// <param name="aSessionId"></param>
1417 /// <param name="aField"></param>
1418 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1420 if (this.InvokeRequired)
1422 //Not in the proper thread, invoke ourselves
1423 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1424 this.Invoke(d, new object[] { aSessionId, aField });
1428 //We are in the proper thread
1429 //Call the non-thread-safe variant
1430 SetClientField(aSessionId, aField);
1437 /// <param name="aSessionId"></param>
1438 /// <param name="aField"></param>
1439 private void SetClientField(string aSessionId, DataField aField)
1441 SetCurrentClient(aSessionId);
1442 ClientData client = iClients[aSessionId];
1445 bool somethingChanged = false;
1447 //Make sure all our fields are in place
1448 while (client.Fields.Count < (aField.Index + 1))
1450 //Add a text field with proper index
1451 client.Fields.Add(new DataField(client.Fields.Count));
1452 somethingChanged = true;
1455 if (client.Fields[aField.Index].IsSameLayout(aField))
1457 //Same layout just update our field
1458 client.Fields[aField.Index] = aField;
1460 if (aField.IsText && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1462 //Text field control already in place, just change the text
1463 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1464 somethingChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1465 label.Text = aField.Text;
1466 label.TextAlign = aField.Alignment;
1468 else if (aField.IsBitmap && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1470 somethingChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1471 //Bitmap field control already in place just change the bitmap
1472 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1473 pictureBox.Image = aField.Bitmap;
1477 somethingChanged = true;
1478 //The requested control in our layout it not of the correct type
1479 //Wrong control type, re-create them all
1480 UpdateTableLayoutPanel(iCurrentClientData);
1485 somethingChanged = true;
1486 //Different layout, need to rebuild it
1487 client.Fields[aField.Index] = aField;
1488 UpdateTableLayoutPanel(iCurrentClientData);
1492 if (somethingChanged)
1494 UpdateClientTreeViewNode(client);
1502 /// <param name="aTexts"></param>
1503 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1505 if (this.InvokeRequired)
1507 //Not in the proper thread, invoke ourselves
1508 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1509 this.Invoke(d, new object[] { aSessionId, aFields });
1513 //Put each our text fields in a label control
1514 foreach (DataField field in aFields)
1516 SetClientField(aSessionId, field);
1524 /// <param name="aSessionId"></param>
1525 /// <param name="aName"></param>
1526 public void SetClientNameThreadSafe(string aSessionId, string aName)
1528 if (this.InvokeRequired)
1530 //Not in the proper thread, invoke ourselves
1531 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1532 this.Invoke(d, new object[] { aSessionId, aName });
1536 //We are in the proper thread
1538 ClientData client = iClients[aSessionId];
1542 client.Name = aName;
1543 //Update our tree-view
1544 UpdateClientTreeViewNode(client);
1552 /// <param name="aClient"></param>
1553 private void UpdateClientTreeViewNode(ClientData aClient)
1555 if (aClient == null)
1560 TreeNode node = null;
1561 //Check that our client node already exists
1562 //Get our client root node using its key which is our session ID
1563 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1564 if (nodes.Count()>0)
1566 //We already have a node for that client
1568 //Clear children as we are going to recreate them below
1573 //Client node does not exists create a new one
1574 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1575 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1581 if (aClient.Name != "")
1583 //We have a name, us it as text for our root node
1584 node.Text = aClient.Name;
1585 //Add a child with SessionId
1586 node.Nodes.Add(new TreeNode(aClient.SessionId));
1590 //No name, use session ID instead
1591 node.Text = aClient.SessionId;
1594 if (aClient.Fields.Count > 0)
1596 //Create root node for our texts
1597 TreeNode textsRoot = new TreeNode("Fields");
1598 node.Nodes.Add(textsRoot);
1599 //For each text add a new entry
1600 foreach (DataField field in aClient.Fields)
1602 if (!field.IsBitmap)
1604 DataField textField = (DataField)field;
1605 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1609 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1619 /// Update our table layout row styles to make sure each rows have similar height
1621 private void UpdateTableLayoutRowStyles()
1623 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1625 rowStyle.SizeType = SizeType.Percent;
1626 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1631 /// Update our display table layout.
1633 /// <param name="aLayout"></param>
1634 private void UpdateTableLayoutPanel(ClientData aClient)
1636 if (aClient == null)
1643 TableLayout layout = aClient.Layout;
1646 tableLayoutPanel.Controls.Clear();
1647 tableLayoutPanel.RowStyles.Clear();
1648 tableLayoutPanel.ColumnStyles.Clear();
1649 tableLayoutPanel.RowCount = 0;
1650 tableLayoutPanel.ColumnCount = 0;
1652 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1654 tableLayoutPanel.RowCount++;
1657 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1659 tableLayoutPanel.ColumnCount++;
1662 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1664 //Create our column styles
1665 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1667 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1671 //Create our row styles
1672 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1675 //Check if we already have a control
1676 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1677 if (existingControl!=null)
1679 //We already have a control in that cell as a results of row/col spanning
1680 //Move on to next cell then
1686 //Check if a client field already exists for that cell
1687 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1689 //No client field specified, create a text field by default
1690 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1693 //Create a control corresponding to the field specified for that cell
1694 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1695 Control control = CreateControlForDataField(field);
1697 //Add newly created control to our table layout at the specified row and column
1698 tableLayoutPanel.Controls.Add(control, i, j);
1699 //Make sure we specify row and column span for that new control
1700 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1701 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1706 while (aClient.Fields.Count > fieldCount)
1708 //We have too much fields for this layout
1709 //Just discard them until we get there
1710 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1717 /// Check our type of data field and create corresponding control
1719 /// <param name="aField"></param>
1720 private Control CreateControlForDataField(DataField aField)
1722 Control control=null;
1723 if (!aField.IsBitmap)
1725 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1726 label.AutoEllipsis = true;
1727 label.AutoSize = true;
1728 label.BackColor = System.Drawing.Color.Transparent;
1729 label.Dock = System.Windows.Forms.DockStyle.Fill;
1730 label.Location = new System.Drawing.Point(1, 1);
1731 label.Margin = new System.Windows.Forms.Padding(0);
1732 label.Name = "marqueeLabel" + aField.Index;
1733 label.OwnTimer = false;
1734 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1735 label.Separator = cds.Separator;
1736 label.MinFontSize = cds.MinFontSize;
1737 label.ScaleToFit = cds.ScaleToFit;
1738 //control.Size = new System.Drawing.Size(254, 30);
1739 //control.TabIndex = 2;
1740 label.Font = cds.Font;
1742 label.TextAlign = aField.Alignment;
1743 label.UseCompatibleTextRendering = true;
1744 label.Text = aField.Text;
1750 //Create picture box
1751 PictureBox picture = new PictureBox();
1752 picture.AutoSize = true;
1753 picture.BackColor = System.Drawing.Color.Transparent;
1754 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1755 picture.Location = new System.Drawing.Point(1, 1);
1756 picture.Margin = new System.Windows.Forms.Padding(0);
1757 picture.Name = "pictureBox" + aField;
1759 picture.Image = aField.Bitmap;
1768 /// Called when the user selected a new display type.
1770 /// <param name="sender"></param>
1771 /// <param name="e"></param>
1772 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1774 //Store the selected display type in our settings
1775 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1776 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1777 Properties.Settings.Default.Save();
1779 //Try re-opening the display connection if we were already connected.
1780 //Otherwise just update our status to reflect display type change.
1781 if (iDisplay.IsOpen())
1783 OpenDisplayConnection();
1791 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1793 if (maskedTextBoxTimerInterval.Text != "")
1795 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1799 timer.Interval = interval;
1800 cds.TimerInterval = timer.Interval;
1801 Properties.Settings.Default.Save();
1806 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1808 if (maskedTextBoxMinFontSize.Text != "")
1810 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1812 if (minFontSize > 0)
1814 cds.MinFontSize = minFontSize;
1815 Properties.Settings.Default.Save();
1816 //We need to recreate our layout for that change to take effect
1817 UpdateTableLayoutPanel(iCurrentClientData);
1823 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1825 if (maskedTextBoxScrollingSpeed.Text != "")
1827 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1829 if (scrollingSpeed > 0)
1831 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1832 Properties.Settings.Default.Save();
1833 //We need to recreate our layout for that change to take effect
1834 UpdateTableLayoutPanel(iCurrentClientData);
1839 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1841 cds.Separator = textBoxScrollLoopSeparator.Text;
1842 Properties.Settings.Default.Save();
1844 //Update our text fields
1845 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1847 ctrl.Separator = cds.Separator;
1852 private void buttonPowerOn_Click(object sender, EventArgs e)
1857 private void buttonPowerOff_Click(object sender, EventArgs e)
1859 iDisplay.PowerOff();
1862 private void buttonShowClock_Click(object sender, EventArgs e)
1867 private void buttonHideClock_Click(object sender, EventArgs e)
1872 private void buttonUpdate_Click(object sender, EventArgs e)
1874 InstallUpdateSyncWithInfo();
1882 if (!iDisplay.IsOpen())
1887 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1888 iSkipFrameRendering = true;
1891 iDisplay.SwapBuffers();
1892 //Then show our clock
1893 iDisplay.ShowClock();
1901 if (!iDisplay.IsOpen())
1906 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
1907 iSkipFrameRendering = false;
1908 iDisplay.HideClock();
1911 private void InstallUpdateSyncWithInfo()
1913 UpdateCheckInfo info = null;
1915 if (ApplicationDeployment.IsNetworkDeployed)
1917 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
1921 info = ad.CheckForDetailedUpdate();
1924 catch (DeploymentDownloadException dde)
1926 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);
1929 catch (InvalidDeploymentException ide)
1931 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);
1934 catch (InvalidOperationException ioe)
1936 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
1940 if (info.UpdateAvailable)
1942 Boolean doUpdate = true;
1944 if (!info.IsUpdateRequired)
1946 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
1947 if (!(DialogResult.OK == dr))
1954 // Display a message that the app MUST reboot. Display the minimum required version.
1955 MessageBox.Show("This application has detected a mandatory update from your current " +
1956 "version to version " + info.MinimumRequiredVersion.ToString() +
1957 ". The application will now install the update and restart.",
1958 "Update Available", MessageBoxButtons.OK,
1959 MessageBoxIcon.Information);
1967 MessageBox.Show("The application has been upgraded, and will now restart.");
1968 Application.Restart();
1970 catch (DeploymentDownloadException dde)
1972 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
1979 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
1988 private void SysTrayHideShow()
1994 WindowState = FormWindowState.Normal;
1999 /// Use to handle minimize events.
2001 /// <param name="sender"></param>
2002 /// <param name="e"></param>
2003 private void MainForm_SizeChanged(object sender, EventArgs e)
2005 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2018 /// <param name="sender"></param>
2019 /// <param name="e"></param>
2020 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2022 //Our table layout size has changed which means our display size has changed.
2023 //We need to re-create our bitmap.
2024 iCreateBitmap = true;
2029 /// A UI thread copy of a client relevant data.
2030 /// Keeping this copy in the UI thread helps us deal with threading issues.
2032 public class ClientData
2034 public ClientData(string aSessionId, ICallback aCallback)
2036 SessionId = aSessionId;
2038 Fields = new List<DataField>();
2039 Layout = new TableLayout(1, 2); //Default to one column and two rows
2040 Callback = aCallback;
2043 public string SessionId { get; set; }
2044 public string Name { get; set; }
2045 public List<DataField> Fields { get; set; }
2046 public TableLayout Layout { get; set; }
2047 public ICallback Callback { get; set; }