Adding Visual Studio Setup project.
2 // Copyright (C) 2014-2015 Stéphane Lenclud.
4 // This file is part of SharpDisplayManager.
6 // SharpDisplayManager is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // SharpDisplayManager is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with SharpDisplayManager. If not, see <http://www.gnu.org/licenses/>.
21 using System.Collections.Generic;
22 using System.ComponentModel;
27 using System.Threading.Tasks;
28 using System.Windows.Forms;
30 using CodeProject.Dialog;
31 using System.Drawing.Imaging;
32 using System.ServiceModel;
33 using System.Threading;
34 using System.Diagnostics;
35 using System.Deployment.Application;
36 using System.Reflection;
38 using NAudio.CoreAudioApi;
39 using NAudio.CoreAudioApi.Interfaces;
40 using System.Runtime.InteropServices;
44 using SharpDisplayClient;
46 using MiniDisplayInterop;
49 namespace SharpDisplayManager
52 public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel);
53 public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt);
54 //Delegates are used for our thread safe method
55 public delegate void AddClientDelegate(string aSessionId, ICallback aCallback);
56 public delegate void RemoveClientDelegate(string aSessionId);
57 public delegate void SetFieldDelegate(string SessionId, DataField aField);
58 public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList<DataField> aFields);
59 public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout);
60 public delegate void SetClientNameDelegate(string aSessionId, string aName);
61 public delegate void PlainUpdateDelegate();
62 public delegate void WndProcDelegate(ref Message aMessage);
65 /// Our Display manager main form
67 [System.ComponentModel.DesignerCategory("Form")]
68 public partial class MainForm : MainFormHid, IMMNotificationClient
70 DateTime LastTickTime;
72 System.Drawing.Bitmap iBmp;
73 bool iCreateBitmap; //Workaround render to bitmap issues when minimized
74 ServiceHost iServiceHost;
75 // Our collection of clients sorted by session id.
76 public Dictionary<string, ClientData> iClients;
77 // The name of the client which informations are currently displayed.
78 public string iCurrentClientSessionId;
79 ClientData iCurrentClientData;
83 public bool iSkipFrameRendering;
84 //Function pointer for pixel color filtering
85 ColorProcessingDelegate iColorFx;
86 //Function pointer for pixel X coordinate intercept
87 CoordinateTranslationDelegate iScreenX;
88 //Function pointer for pixel Y coordinate intercept
89 CoordinateTranslationDelegate iScreenY;
91 private MMDeviceEnumerator iMultiMediaDeviceEnumerator;
92 private MMDevice iMultiMediaDevice;
94 private NetworkManager iNetworkManager;
97 /// CEC - Consumer Electronic Control.
98 /// Notably used to turn TV on and off as Windows broadcast monitor on and off notifications.
100 private ConsumerElectronicControl iCecManager;
103 /// Manage run when Windows startup option
105 private StartupManager iStartupManager;
108 /// System tray icon.
110 private NotifyIconAdv iNotifyIcon;
113 /// Allow user to receive window messages;
115 public event WndProcDelegate OnWndProc;
119 iSkipFrameRendering = false;
121 iCurrentClientSessionId = "";
122 iCurrentClientData = null;
123 LastTickTime = DateTime.Now;
124 //Instantiate our display and register for events notifications
125 iDisplay = new Display();
126 iDisplay.OnOpened += OnDisplayOpened;
127 iDisplay.OnClosed += OnDisplayClosed;
129 iClients = new Dictionary<string, ClientData>();
130 iStartupManager = new StartupManager();
131 iNotifyIcon = new NotifyIconAdv();
133 //Have our designer initialize its controls
134 InitializeComponent();
136 //Populate device types
137 PopulateDeviceTypes();
139 //Populate optical drives
140 PopulateOpticalDrives();
142 //Initial status update
145 //We have a bug when drawing minimized and reusing our bitmap
146 //Though I could not reproduce it on Windows 10
147 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
148 iCreateBitmap = false;
150 //Minimize our window if desired
151 if (Properties.Settings.Default.StartMinimized)
153 WindowState = FormWindowState.Minimized;
161 /// <param name="sender"></param>
162 /// <param name="e"></param>
163 private void MainForm_Load(object sender, EventArgs e)
165 //Check if we are running a Click Once deployed application
166 if (ApplicationDeployment.IsNetworkDeployed)
168 //This is a proper Click Once installation, fetch and show our version number
169 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
173 //Not a proper Click Once installation, assuming development build then
174 this.Text += " - development";
178 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
179 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
180 UpdateAudioDeviceAndMasterVolumeThreadSafe();
183 iNetworkManager = new NetworkManager();
184 iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
185 UpdateNetworkStatus();
188 iCecManager = new ConsumerElectronicControl();
189 OnWndProc += iCecManager.OnWndProc;
193 //Setup notification icon
196 // To make sure start up with minimize to tray works
197 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
203 //When not debugging we want the screen to be empty until a client takes over
206 //When developing we want at least one client for testing
207 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
210 //Open display connection on start-up if needed
211 if (Properties.Settings.Default.DisplayConnectOnStartup)
213 OpenDisplayConnection();
216 //Start our server so that we can get client requests
219 //Register for HID events
220 RegisterHidDevices();
224 /// Called when our display is opened.
226 /// <param name="aDisplay"></param>
227 private void OnDisplayOpened(Display aDisplay)
229 //Make sure we resume frame rendering
230 iSkipFrameRendering = false;
232 //Set our screen size now that our display is connected
233 //Our panelDisplay is the container of our tableLayoutPanel
234 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
235 //panelDisplay needs an extra 2 pixels for borders on each sides
236 //tableLayoutPanel will eventually be the exact size of our display
237 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
238 panelDisplay.Size = size;
240 //Our display was just opened, update our UI
242 //Initiate asynchronous request
243 iDisplay.RequestFirmwareRevision();
246 UpdateMasterVolumeThreadSafe();
248 UpdateNetworkStatus();
251 //Testing icon in debug, no arm done if icon not supported
252 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
253 //iDisplay.SetAllIconsStatus(2);
259 /// Called when our display is closed.
261 /// <param name="aDisplay"></param>
262 private void OnDisplayClosed(Display aDisplay)
264 //Our display was just closed, update our UI consequently
268 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
270 //Update network status
271 UpdateNetworkStatus();
275 /// Update our Network Status
277 private void UpdateNetworkStatus()
279 if (iDisplay.IsOpen())
281 iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
282 iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
287 int iLastNetworkIconIndex = 0;
288 int iUpdateCountSinceLastNetworkAnimation = 0;
293 private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
295 iUpdateCountSinceLastNetworkAnimation++;
296 iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
298 if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
300 int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal);
303 //Prevents div by zero and other undefined behavior
306 iLastNetworkIconIndex++;
307 iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2);
308 for (int i=0;i<iconCount;i++)
310 if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && !(i == 1 && iLastNetworkIconIndex > 4))
312 iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i);
316 iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i);
325 /// Receive volume change notification and reflect changes on our slider.
327 /// <param name="data"></param>
328 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
330 UpdateMasterVolumeThreadSafe();
334 /// Update master volume when user moves our slider.
336 /// <param name="sender"></param>
337 /// <param name="e"></param>
338 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
340 //Just like Windows Volume Mixer we unmute if the volume is adjusted
341 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
342 //Set volume level according to our volume slider new position
343 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
348 /// Mute check box changed.
350 /// <param name="sender"></param>
351 /// <param name="e"></param>
352 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
354 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
358 /// Device State Changed
360 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
365 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
370 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
373 /// Default Device Changed
375 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
377 if (role == Role.Multimedia && flow == DataFlow.Render)
379 UpdateAudioDeviceAndMasterVolumeThreadSafe();
384 /// Property Value Changed
386 /// <param name="pwstrDeviceId"></param>
387 /// <param name="key"></param>
388 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
394 /// Update master volume indicators based our current system states.
395 /// This typically includes volume levels and mute status.
397 private void UpdateMasterVolumeThreadSafe()
399 if (this.InvokeRequired)
401 //Not in the proper thread, invoke ourselves
402 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
403 this.Invoke(d, new object[] { });
407 //Update volume slider
408 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
409 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
410 //Update mute checkbox
411 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
413 //If our display connection is open we need to update its icons
414 if (iDisplay.IsOpen())
416 //First take care our our volume level icons
417 int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume);
418 if (volumeIconCount > 0)
420 //Compute current volume level from system level and the number of segments in our display volume bar.
421 //That tells us how many segments in our volume bar needs to be turned on.
422 float currentVolume = volumeLevelScalar * volumeIconCount;
423 int segmentOnCount = Convert.ToInt32(currentVolume);
424 //Check if our segment count was rounded up, this will later be used for half brightness segment
425 bool roundedUp = segmentOnCount > currentVolume;
427 for (int i = 0; i < volumeIconCount; i++)
429 if (i < segmentOnCount)
431 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
432 if (i == segmentOnCount - 1 && roundedUp)
435 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1) / 2);
440 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1);
445 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0);
450 //Take care our our mute icon
451 iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iMultiMediaDevice.AudioEndpointVolume.Mute);
459 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
461 if (this.InvokeRequired)
463 //Not in the proper thread, invoke ourselves
464 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
465 this.Invoke(d, new object[] { });
469 //We are in the correct thread just go ahead.
472 //Get our master volume
473 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
475 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
477 //Show our volume in our track bar
478 UpdateMasterVolumeThreadSafe();
480 //Register to get volume modifications
481 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
483 trackBarMasterVolume.Enabled = true;
487 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
488 Debug.WriteLine(ex.ToString());
489 //Something went wrong S/PDIF device ca throw exception I guess
490 trackBarMasterVolume.Enabled = false;
497 private void PopulateDeviceTypes()
499 int count = Display.TypeCount();
501 for (int i = 0; i < count; i++)
503 comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type)i));
510 private void PopulateOpticalDrives()
512 //Reset our list of drives
513 comboBoxOpticalDrives.Items.Clear();
514 comboBoxOpticalDrives.Items.Add("None");
516 //Go through each drives on our system and collected the optical ones in our list
517 DriveInfo[] allDrives = DriveInfo.GetDrives();
518 foreach (DriveInfo d in allDrives)
520 Debug.WriteLine("Drive " + d.Name);
521 Debug.WriteLine(" Drive type: {0}", d.DriveType);
523 if (d.DriveType==DriveType.CDRom)
525 //This is an optical drive, add it now
526 comboBoxOpticalDrives.Items.Add(d.Name.Substring(0,2));
534 /// <returns></returns>
535 public string OpticalDriveToEject()
537 return comboBoxOpticalDrives.SelectedItem.ToString();
545 private void SetupTrayIcon()
547 iNotifyIcon.Icon = GetIcon("vfd.ico");
548 iNotifyIcon.Text = "Sharp Display Manager";
549 iNotifyIcon.Visible = true;
551 //Double click toggles visibility - typically brings up the application
552 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
557 //Adding a context menu, useful to be able to exit the application
558 ContextMenu contextMenu = new ContextMenu();
559 //Context menu item to toggle visibility
560 MenuItem hideShowItem = new MenuItem("Hide/Show");
561 hideShowItem.Click += delegate(object obj, EventArgs args)
565 contextMenu.MenuItems.Add(hideShowItem);
567 //Context menu item separator
568 contextMenu.MenuItems.Add(new MenuItem("-"));
570 //Context menu exit item
571 MenuItem exitItem = new MenuItem("Exit");
572 exitItem.Click += delegate(object obj, EventArgs args)
576 contextMenu.MenuItems.Add(exitItem);
578 iNotifyIcon.ContextMenu = contextMenu;
582 /// Access icons from embedded resources.
584 /// <param name="name"></param>
585 /// <returns></returns>
586 public static Icon GetIcon(string name)
588 name = "SharpDisplayManager.Resources." + name;
591 Assembly.GetExecutingAssembly().GetManifestResourceNames();
592 for (int i = 0; i < names.Length; i++)
594 if (names[i].Replace('\\', '.') == name)
596 using (Stream stream = Assembly.GetExecutingAssembly().
597 GetManifestResourceStream(names[i]))
599 return new Icon(stream);
609 /// Set our current client.
610 /// This will take care of applying our client layout and set data fields.
612 /// <param name="aSessionId"></param>
613 void SetCurrentClient(string aSessionId, bool aForce=false)
615 if (aSessionId == iCurrentClientSessionId)
617 //Given client is already the current one.
618 //Don't bother changing anything then.
623 //Check when was the last time we switched to that client
624 if (iCurrentClientData != null)
626 double lastSwitchToClientSecondsAgo = (DateTime.Now - iCurrentClientData.LastSwitchTime).TotalSeconds;
627 //TODO: put that hard coded value as a client property
628 //Clients should be able to define how often they can be interrupted
629 //Thus a background client can set this to zero allowing any other client to interrupt at any time
630 //We could also compute this delay by looking at the requests frequencies?
631 if (!aForce && (lastSwitchToClientSecondsAgo < 30)) //Make sure a client is on for at least 30 seconds
633 //Don't switch clients too often
638 //Set current client ID.
639 iCurrentClientSessionId = aSessionId;
640 //Set the time we last switched to that client
641 iClients[aSessionId].LastSwitchTime = DateTime.Now;
642 //Fetch and set current client data.
643 iCurrentClientData = iClients[aSessionId];
644 //Apply layout and set data fields.
645 UpdateTableLayoutPanel(iCurrentClientData);
648 private void buttonFont_Click(object sender, EventArgs e)
650 //fontDialog.ShowColor = true;
651 //fontDialog.ShowApply = true;
652 fontDialog.ShowEffects = true;
653 fontDialog.Font = cds.Font;
655 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
657 //fontDialog.ShowHelp = true;
659 //fontDlg.MaxSize = 40;
660 //fontDlg.MinSize = 22;
662 //fontDialog.Parent = this;
663 //fontDialog.StartPosition = FormStartPosition.CenterParent;
665 //DlgBox.ShowDialog(fontDialog);
667 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
668 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
670 //Set the fonts to all our labels in our layout
671 foreach (Control ctrl in tableLayoutPanel.Controls)
673 if (ctrl is MarqueeLabel)
675 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
680 cds.Font = fontDialog.Font;
681 Properties.Settings.Default.Save();
690 void CheckFontHeight()
692 //Show font height and width
693 labelFontHeight.Text = "Font height: " + cds.Font.Height;
694 float charWidth = IsFixedWidth(cds.Font);
695 if (charWidth == 0.0f)
697 labelFontWidth.Visible = false;
701 labelFontWidth.Visible = true;
702 labelFontWidth.Text = "Font width: " + charWidth;
705 MarqueeLabel label = null;
706 //Get the first label control we can find
707 foreach (Control ctrl in tableLayoutPanel.Controls)
709 if (ctrl is MarqueeLabel)
711 label = (MarqueeLabel)ctrl;
716 //Now check font height and show a warning if needed.
717 if (label != null && label.Font.Height > label.Height)
719 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
720 labelWarning.Visible = true;
724 labelWarning.Visible = false;
729 private void buttonCapture_Click(object sender, EventArgs e)
731 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
732 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
733 //Bitmap bmpToSave = new Bitmap(bmp);
734 bmp.Save("D:\\capture.png");
736 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
739 string outputFileName = "d:\\capture.png";
740 using (MemoryStream memory = new MemoryStream())
742 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
744 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
745 byte[] bytes = memory.ToArray();
746 fs.Write(bytes, 0, bytes.Length);
753 private void CheckForRequestResults()
755 if (iDisplay.IsRequestPending())
757 switch (iDisplay.AttemptRequestCompletion())
759 case MiniDisplay.Request.FirmwareRevision:
760 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
761 //Issue next request then
762 iDisplay.RequestPowerSupplyStatus();
765 case MiniDisplay.Request.PowerSupplyStatus:
766 if (iDisplay.PowerSupplyStatus())
768 toolStripStatusLabelPower.Text = "ON";
772 toolStripStatusLabelPower.Text = "OFF";
774 //Issue next request then
775 iDisplay.RequestDeviceId();
778 case MiniDisplay.Request.DeviceId:
779 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
780 //No more request to issue
786 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
788 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
795 public static uint ColorUntouched(int aX, int aY, uint aPixel)
800 public static uint ColorInversed(int aX, int aY, uint aPixel)
805 public static uint ColorChessboard(int aX, int aY, uint aPixel)
807 if ((aX % 2 == 0) && (aY % 2 == 0))
811 else if ((aX % 2 != 0) && (aY % 2 != 0))
819 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
821 return aBmp.Width - aX - 1;
824 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
826 return iBmp.Height - aY - 1;
829 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
834 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
840 /// Select proper pixel delegates according to our current settings.
842 private void SetupPixelDelegates()
844 //Select our pixel processing routine
845 if (cds.InverseColors)
847 //iColorFx = ColorChessboard;
848 iColorFx = ColorInversed;
852 iColorFx = ColorWhiteIsOn;
855 //Select proper coordinate translation functions
856 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
857 if (cds.ReverseScreen)
859 iScreenX = ScreenReversedX;
860 iScreenY = ScreenReversedY;
870 //This is our timer tick responsible to perform our render
871 private void timer_Tick(object sender, EventArgs e)
873 //Update our animations
874 DateTime NewTickTime = DateTime.Now;
876 UpdateNetworkSignal(LastTickTime, NewTickTime);
878 //Update animation for all our marquees
879 foreach (Control ctrl in tableLayoutPanel.Controls)
881 if (ctrl is MarqueeLabel)
883 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
888 if (iDisplay.IsOpen())
890 CheckForRequestResults();
892 //Check if frame rendering is needed
893 //Typically used when showing clock
894 if (!iSkipFrameRendering)
899 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
900 iCreateBitmap = false;
902 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
903 //iBmp.Save("D:\\capture.png");
905 //Send it to our display
906 for (int i = 0; i < iBmp.Width; i++)
908 for (int j = 0; j < iBmp.Height; j++)
912 //Get our processed pixel coordinates
913 int x = iScreenX(iBmp, i);
914 int y = iScreenY(iBmp, j);
916 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
917 //Apply color effects
918 color = iColorFx(x, y, color);
920 iDisplay.SetPixel(x, y, color);
925 iDisplay.SwapBuffers();
929 //Compute instant FPS
930 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
932 LastTickTime = NewTickTime;
937 /// Attempt to establish connection with our display hardware.
939 private void OpenDisplayConnection()
941 CloseDisplayConnection();
943 if (!iDisplay.Open((MiniDisplay.Type)cds.DisplayType))
946 toolStripStatusLabelConnect.Text = "Connection error";
950 private void CloseDisplayConnection()
952 //Status will be updated upon receiving the closed event
954 if (iDisplay == null || !iDisplay.IsOpen())
959 //Do not clear if we gave up on rendering already.
960 //This means we will keep on displaying clock on MDM166AA for instance.
961 if (!iSkipFrameRendering)
964 iDisplay.SwapBuffers();
967 iDisplay.SetAllIconsStatus(0); //Turn off all icons
971 private void buttonOpen_Click(object sender, EventArgs e)
973 OpenDisplayConnection();
976 private void buttonClose_Click(object sender, EventArgs e)
978 CloseDisplayConnection();
981 private void buttonClear_Click(object sender, EventArgs e)
984 iDisplay.SwapBuffers();
987 private void buttonFill_Click(object sender, EventArgs e)
990 iDisplay.SwapBuffers();
993 private void trackBarBrightness_Scroll(object sender, EventArgs e)
995 cds.Brightness = trackBarBrightness.Value;
996 Properties.Settings.Default.Save();
997 iDisplay.SetBrightness(trackBarBrightness.Value);
1003 /// CDS stands for Current Display Settings
1005 private DisplaySettings cds
1009 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
1010 if (settings == null)
1012 settings = new DisplaysSettings();
1014 Properties.Settings.Default.DisplaysSettings = settings;
1017 //Make sure all our settings have been created
1018 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
1020 settings.Displays.Add(new DisplaySettings());
1023 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
1024 return displaySettings;
1029 /// Check if the given font has a fixed character pitch.
1031 /// <param name="ft"></param>
1032 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
1033 public float IsFixedWidth(Font ft)
1035 Graphics g = CreateGraphics();
1036 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
1037 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
1039 bool fixedWidth = true;
1041 foreach (char c in charSizes)
1042 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
1054 /// Synchronize UI with settings
1056 private void UpdateStatus()
1059 checkBoxShowBorders.Checked = cds.ShowBorders;
1060 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1062 //Set the proper font to each of our labels
1063 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1065 ctrl.Font = cds.Font;
1069 //Check if "run on Windows startup" is enabled
1070 checkBoxAutoStart.Checked = iStartupManager.Startup;
1072 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
1073 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
1074 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
1075 labelStartFileName.Text = Properties.Settings.Default.StartFileName;
1077 //Try find our drive in our drive list
1078 int opticalDriveItemIndex=0;
1079 bool driveNotFound = true;
1080 string opticalDriveToEject=Properties.Settings.Default.OpticalDriveToEject;
1081 foreach (object item in comboBoxOpticalDrives.Items)
1083 if (opticalDriveToEject == item.ToString())
1085 comboBoxOpticalDrives.SelectedIndex = opticalDriveItemIndex;
1086 driveNotFound = false;
1089 opticalDriveItemIndex++;
1094 //We could not find the drive we had saved.
1095 //Select "None" then.
1096 comboBoxOpticalDrives.SelectedIndex = 0;
1100 checkBoxCecEnabled.Checked = Properties.Settings.Default.CecEnabled;
1101 checkBoxCecMonitorOn.Checked = Properties.Settings.Default.CecMonitorOn;
1102 checkBoxCecMonitorOff.Checked = Properties.Settings.Default.CecMonitorOff;
1103 comboBoxHdmiPort.SelectedIndex = Properties.Settings.Default.CecHdmiPort - 1;
1105 //Mini Display settings
1106 checkBoxReverseScreen.Checked = cds.ReverseScreen;
1107 checkBoxInverseColors.Checked = cds.InverseColors;
1108 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
1109 checkBoxScaleToFit.Checked = cds.ScaleToFit;
1110 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1111 labelMinFontSize.Enabled = cds.ScaleToFit;
1112 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
1113 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
1114 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
1115 timer.Interval = cds.TimerInterval;
1116 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
1117 textBoxScrollLoopSeparator.Text = cds.Separator;
1119 SetupPixelDelegates();
1121 if (iDisplay.IsOpen())
1123 //We have a display connection
1124 //Reflect that in our UI
1126 tableLayoutPanel.Enabled = true;
1127 panelDisplay.Enabled = true;
1129 //Only setup brightness if display is open
1130 trackBarBrightness.Minimum = iDisplay.MinBrightness();
1131 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
1132 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
1134 //Brightness out of range, this can occur when using auto-detect
1135 //Use max brightness instead
1136 trackBarBrightness.Value = iDisplay.MaxBrightness();
1137 iDisplay.SetBrightness(iDisplay.MaxBrightness());
1141 trackBarBrightness.Value = cds.Brightness;
1142 iDisplay.SetBrightness(cds.Brightness);
1145 //Try compute the steps to something that makes sense
1146 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
1147 trackBarBrightness.SmallChange = 1;
1150 buttonFill.Enabled = true;
1151 buttonClear.Enabled = true;
1152 buttonOpen.Enabled = false;
1153 buttonClose.Enabled = true;
1154 trackBarBrightness.Enabled = true;
1155 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1156 //+ " - " + iDisplay.SerialNumber();
1158 if (iDisplay.SupportPowerOnOff())
1160 buttonPowerOn.Enabled = true;
1161 buttonPowerOff.Enabled = true;
1165 buttonPowerOn.Enabled = false;
1166 buttonPowerOff.Enabled = false;
1169 if (iDisplay.SupportClock())
1171 buttonShowClock.Enabled = true;
1172 buttonHideClock.Enabled = true;
1176 buttonShowClock.Enabled = false;
1177 buttonHideClock.Enabled = false;
1181 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1182 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel)>0;
1184 if (cds.ShowVolumeLabel)
1186 iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
1190 iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
1195 //Display is connection not available
1196 //Reflect that in our UI
1197 checkBoxShowVolumeLabel.Enabled = false;
1198 tableLayoutPanel.Enabled = false;
1199 panelDisplay.Enabled = false;
1200 buttonFill.Enabled = false;
1201 buttonClear.Enabled = false;
1202 buttonOpen.Enabled = true;
1203 buttonClose.Enabled = false;
1204 trackBarBrightness.Enabled = false;
1205 buttonPowerOn.Enabled = false;
1206 buttonPowerOff.Enabled = false;
1207 buttonShowClock.Enabled = false;
1208 buttonHideClock.Enabled = false;
1209 toolStripStatusLabelConnect.Text = "Disconnected";
1210 toolStripStatusLabelPower.Text = "N/A";
1219 /// <param name="sender"></param>
1220 /// <param name="e"></param>
1221 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1223 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1224 Properties.Settings.Default.Save();
1228 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1230 //Save our show borders setting
1231 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1232 cds.ShowBorders = checkBoxShowBorders.Checked;
1233 Properties.Settings.Default.Save();
1237 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1239 //Save our connect on startup setting
1240 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1241 Properties.Settings.Default.Save();
1244 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1246 //Save our "Minimize to tray" setting
1247 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1248 Properties.Settings.Default.Save();
1252 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1254 //Save our "Start minimized" setting
1255 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1256 Properties.Settings.Default.Save();
1259 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1261 iStartupManager.Startup = checkBoxAutoStart.Checked;
1265 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1267 //Save our reverse screen setting
1268 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1269 Properties.Settings.Default.Save();
1270 SetupPixelDelegates();
1273 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1275 //Save our inverse colors setting
1276 cds.InverseColors = checkBoxInverseColors.Checked;
1277 Properties.Settings.Default.Save();
1278 SetupPixelDelegates();
1281 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1283 //Save our scale to fit setting
1284 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1285 Properties.Settings.Default.Save();
1287 labelMinFontSize.Enabled = cds.ScaleToFit;
1288 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1291 private void MainForm_Resize(object sender, EventArgs e)
1293 if (WindowState == FormWindowState.Minimized)
1295 // To workaround our empty bitmap bug on Windows 7 we need to recreate our bitmap when the application is minimized
1296 // That's apparently not needed on Windows 10 but we better leave it in place.
1297 iCreateBitmap = true;
1301 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1304 iNetworkManager.Dispose();
1305 CloseDisplayConnection();
1307 e.Cancel = iClosing;
1310 public void StartServer()
1312 iServiceHost = new ServiceHost
1315 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1318 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1319 iServiceHost.Open();
1322 public void StopServer()
1324 if (iClients.Count > 0 && !iClosing)
1328 BroadcastCloseEvent();
1332 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1334 iClosing = false; //We make sure we force close if asked twice
1339 //We removed that as it often lags for some reason
1340 //iServiceHost.Close();
1344 public void BroadcastCloseEvent()
1346 Trace.TraceInformation("BroadcastCloseEvent - start");
1348 var inactiveClients = new List<string>();
1349 foreach (var client in iClients)
1351 //if (client.Key != eventData.ClientName)
1355 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1356 client.Value.Callback.OnCloseOrder(/*eventData*/);
1358 catch (Exception ex)
1360 inactiveClients.Add(client.Key);
1365 if (inactiveClients.Count > 0)
1367 foreach (var client in inactiveClients)
1369 iClients.Remove(client);
1370 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1374 if (iClients.Count==0)
1381 /// Just remove all our fields.
1383 private void ClearLayout()
1385 tableLayoutPanel.Controls.Clear();
1386 tableLayoutPanel.RowStyles.Clear();
1387 tableLayoutPanel.ColumnStyles.Clear();
1388 iCurrentClientData = null;
1392 /// Just launch a demo client.
1394 private void StartNewClient(string aTopText = "", string aBottomText = "")
1396 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1397 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1398 clientThread.Start(myParams);
1402 private void buttonStartClient_Click(object sender, EventArgs e)
1407 private void buttonSuspend_Click(object sender, EventArgs e)
1409 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1410 timer.Enabled = !timer.Enabled;
1413 buttonSuspend.Text = "Run";
1417 buttonSuspend.Text = "Pause";
1421 private void buttonCloseClients_Click(object sender, EventArgs e)
1423 BroadcastCloseEvent();
1426 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1428 //Root node must have at least one child
1429 if (e.Node.Nodes.Count == 0)
1434 //If the selected node is the root node of a client then switch to it
1435 string sessionId=e.Node.Nodes[0].Text; //First child of a root node is the sessionId
1436 if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at
1438 //We have a valid session just switch to that client
1439 SetCurrentClient(sessionId,true);
1448 /// <param name="aSessionId"></param>
1449 /// <param name="aCallback"></param>
1450 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1452 if (this.InvokeRequired)
1454 //Not in the proper thread, invoke ourselves
1455 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1456 this.Invoke(d, new object[] { aSessionId, aCallback });
1460 //We are in the proper thread
1461 //Add this session to our collection of clients
1462 ClientData newClient = new ClientData(aSessionId, aCallback);
1463 Program.iMainForm.iClients.Add(aSessionId, newClient);
1464 //Add this session to our UI
1465 UpdateClientTreeViewNode(newClient);
1472 /// <param name="aSessionId"></param>
1473 public void RemoveClientThreadSafe(string aSessionId)
1475 if (this.InvokeRequired)
1477 //Not in the proper thread, invoke ourselves
1478 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1479 this.Invoke(d, new object[] { aSessionId });
1483 //We are in the proper thread
1484 //Remove this session from both client collection and UI tree view
1485 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1487 Program.iMainForm.iClients.Remove(aSessionId);
1488 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1491 if (iClients.Count == 0)
1493 //Clear our screen when last client disconnects
1498 //We were closing our form
1499 //All clients are now closed
1500 //Just resume our close operation
1511 /// <param name="aSessionId"></param>
1512 /// <param name="aLayout"></param>
1513 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1515 if (this.InvokeRequired)
1517 //Not in the proper thread, invoke ourselves
1518 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1519 this.Invoke(d, new object[] { aSessionId, aLayout });
1523 ClientData client = iClients[aSessionId];
1526 //Don't change a thing if the layout is the same
1527 if (!client.Layout.IsSameAs(aLayout))
1529 Debug.Print("SetClientLayoutThreadSafe: Layout updated.");
1530 //Set our client layout then
1531 client.Layout = aLayout;
1532 //Layout has changed clear our fields then
1533 client.Fields.Clear();
1535 UpdateClientTreeViewNode(client);
1539 Debug.Print("SetClientLayoutThreadSafe: Layout has not changed.");
1548 /// <param name="aSessionId"></param>
1549 /// <param name="aField"></param>
1550 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1552 if (this.InvokeRequired)
1554 //Not in the proper thread, invoke ourselves
1555 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1556 this.Invoke(d, new object[] { aSessionId, aField });
1560 //We are in the proper thread
1561 //Call the non-thread-safe variant
1562 SetClientField(aSessionId, aField);
1569 /// <param name="aSessionId"></param>
1570 /// <param name="aField"></param>
1571 private void SetClientField(string aSessionId, DataField aField)
1573 //TODO: should check if the field actually changed?
1575 ClientData client = iClients[aSessionId];
1576 bool layoutChanged = false;
1577 bool contentChanged = true;
1579 //Make sure all our fields are in place
1580 while (client.Fields.Count < (aField.Index + 1))
1582 //Add a data field with proper index
1583 client.Fields.Add(new DataField(client.Fields.Count));
1584 layoutChanged = true;
1587 //Now that we know our fields are in place just update that one
1588 client.Fields[aField.Index] = aField;
1590 if (client.Fields[aField.Index].IsSameLayout(aField))
1592 //If we are updating a field in our current client we need to update it in our panel
1593 if (aSessionId == iCurrentClientSessionId)
1595 if (aField.IsText && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1597 //Text field control already in place, just change the text
1598 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1599 contentChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1600 label.Text = aField.Text;
1601 label.TextAlign = aField.Alignment;
1603 else if (aField.IsBitmap && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1605 contentChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1606 //Bitmap field control already in place just change the bitmap
1607 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1608 pictureBox.Image = aField.Bitmap;
1612 layoutChanged = true;
1618 layoutChanged = true;
1621 //If either content or layout changed we need to update our tree view to reflect the changes
1622 if (contentChanged || layoutChanged)
1624 UpdateClientTreeViewNode(client);
1628 Debug.Print("Layout changed");
1629 //Our layout has changed, if we are already the current client we need to update our panel
1630 if (aSessionId == iCurrentClientSessionId)
1632 //Apply layout and set data fields.
1633 UpdateTableLayoutPanel(iCurrentClientData);
1638 Debug.Print("Layout has not changed.");
1643 Debug.Print("WARNING: content and layout have not changed!");
1646 //When a client field is set we try switching to this client to present the new information to our user
1647 SetCurrentClient(aSessionId);
1653 /// <param name="aTexts"></param>
1654 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1656 if (this.InvokeRequired)
1658 //Not in the proper thread, invoke ourselves
1659 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1660 this.Invoke(d, new object[] { aSessionId, aFields });
1664 //Put each our text fields in a label control
1665 foreach (DataField field in aFields)
1667 SetClientField(aSessionId, field);
1675 /// <param name="aSessionId"></param>
1676 /// <param name="aName"></param>
1677 public void SetClientNameThreadSafe(string aSessionId, string aName)
1679 if (this.InvokeRequired)
1681 //Not in the proper thread, invoke ourselves
1682 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1683 this.Invoke(d, new object[] { aSessionId, aName });
1687 //We are in the proper thread
1689 ClientData client = iClients[aSessionId];
1693 client.Name = aName;
1694 //Update our tree-view
1695 UpdateClientTreeViewNode(client);
1703 /// <param name="aClient"></param>
1704 private void UpdateClientTreeViewNode(ClientData aClient)
1706 Debug.Print("UpdateClientTreeViewNode");
1708 if (aClient == null)
1713 TreeNode node = null;
1714 //Check that our client node already exists
1715 //Get our client root node using its key which is our session ID
1716 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1717 if (nodes.Count()>0)
1719 //We already have a node for that client
1721 //Clear children as we are going to recreate them below
1726 //Client node does not exists create a new one
1727 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1728 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1734 if (aClient.Name != "")
1736 //We have a name, us it as text for our root node
1737 node.Text = aClient.Name;
1738 //Add a child with SessionId
1739 node.Nodes.Add(new TreeNode(aClient.SessionId));
1743 //No name, use session ID instead
1744 node.Text = aClient.SessionId;
1747 if (aClient.Fields.Count > 0)
1749 //Create root node for our texts
1750 TreeNode textsRoot = new TreeNode("Fields");
1751 node.Nodes.Add(textsRoot);
1752 //For each text add a new entry
1753 foreach (DataField field in aClient.Fields)
1755 if (!field.IsBitmap)
1757 DataField textField = (DataField)field;
1758 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1762 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1772 /// Update our table layout row styles to make sure each rows have similar height
1774 private void UpdateTableLayoutRowStyles()
1776 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1778 rowStyle.SizeType = SizeType.Percent;
1779 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1784 /// Update our display table layout.
1786 /// <param name="aLayout"></param>
1787 private void UpdateTableLayoutPanel(ClientData aClient)
1789 Debug.Print("UpdateClientTreeViewNode");
1791 if (aClient == null)
1798 TableLayout layout = aClient.Layout;
1801 //First clean our current panel
1802 tableLayoutPanel.Controls.Clear();
1803 tableLayoutPanel.RowStyles.Clear();
1804 tableLayoutPanel.ColumnStyles.Clear();
1805 tableLayoutPanel.RowCount = 0;
1806 tableLayoutPanel.ColumnCount = 0;
1808 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1810 tableLayoutPanel.RowCount++;
1813 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1815 tableLayoutPanel.ColumnCount++;
1818 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1820 //Create our column styles
1821 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1823 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1827 //Create our row styles
1828 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1831 //Check if we already have a control
1832 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1833 if (existingControl!=null)
1835 //We already have a control in that cell as a results of row/col spanning
1836 //Move on to next cell then
1842 //Check if a client field already exists for that cell
1843 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1845 //No client field specified, create a text field by default
1846 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1849 //Create a control corresponding to the field specified for that cell
1850 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1851 Control control = CreateControlForDataField(field);
1853 //Add newly created control to our table layout at the specified row and column
1854 tableLayoutPanel.Controls.Add(control, i, j);
1855 //Make sure we specify row and column span for that new control
1856 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1857 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1862 while (aClient.Fields.Count > fieldCount)
1864 //We have too much fields for this layout
1865 //Just discard them until we get there
1866 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1873 /// Check our type of data field and create corresponding control
1875 /// <param name="aField"></param>
1876 private Control CreateControlForDataField(DataField aField)
1878 Control control=null;
1879 if (!aField.IsBitmap)
1881 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1882 label.AutoEllipsis = true;
1883 label.AutoSize = true;
1884 label.BackColor = System.Drawing.Color.Transparent;
1885 label.Dock = System.Windows.Forms.DockStyle.Fill;
1886 label.Location = new System.Drawing.Point(1, 1);
1887 label.Margin = new System.Windows.Forms.Padding(0);
1888 label.Name = "marqueeLabel" + aField.Index;
1889 label.OwnTimer = false;
1890 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1891 label.Separator = cds.Separator;
1892 label.MinFontSize = cds.MinFontSize;
1893 label.ScaleToFit = cds.ScaleToFit;
1894 //control.Size = new System.Drawing.Size(254, 30);
1895 //control.TabIndex = 2;
1896 label.Font = cds.Font;
1898 label.TextAlign = aField.Alignment;
1899 label.UseCompatibleTextRendering = true;
1900 label.Text = aField.Text;
1906 //Create picture box
1907 PictureBox picture = new PictureBox();
1908 picture.AutoSize = true;
1909 picture.BackColor = System.Drawing.Color.Transparent;
1910 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1911 picture.Location = new System.Drawing.Point(1, 1);
1912 picture.Margin = new System.Windows.Forms.Padding(0);
1913 picture.Name = "pictureBox" + aField;
1915 picture.Image = aField.Bitmap;
1924 /// Called when the user selected a new display type.
1926 /// <param name="sender"></param>
1927 /// <param name="e"></param>
1928 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1930 //Store the selected display type in our settings
1931 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1932 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1933 Properties.Settings.Default.Save();
1935 //Try re-opening the display connection if we were already connected.
1936 //Otherwise just update our status to reflect display type change.
1937 if (iDisplay.IsOpen())
1939 OpenDisplayConnection();
1947 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1949 if (maskedTextBoxTimerInterval.Text != "")
1951 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1955 timer.Interval = interval;
1956 cds.TimerInterval = timer.Interval;
1957 Properties.Settings.Default.Save();
1962 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1964 if (maskedTextBoxMinFontSize.Text != "")
1966 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1968 if (minFontSize > 0)
1970 cds.MinFontSize = minFontSize;
1971 Properties.Settings.Default.Save();
1972 //We need to recreate our layout for that change to take effect
1973 UpdateTableLayoutPanel(iCurrentClientData);
1979 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1981 if (maskedTextBoxScrollingSpeed.Text != "")
1983 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1985 if (scrollingSpeed > 0)
1987 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1988 Properties.Settings.Default.Save();
1989 //We need to recreate our layout for that change to take effect
1990 UpdateTableLayoutPanel(iCurrentClientData);
1995 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1997 cds.Separator = textBoxScrollLoopSeparator.Text;
1998 Properties.Settings.Default.Save();
2000 //Update our text fields
2001 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
2003 ctrl.Separator = cds.Separator;
2008 private void buttonPowerOn_Click(object sender, EventArgs e)
2013 private void buttonPowerOff_Click(object sender, EventArgs e)
2015 iDisplay.PowerOff();
2018 private void buttonShowClock_Click(object sender, EventArgs e)
2023 private void buttonHideClock_Click(object sender, EventArgs e)
2028 private void buttonUpdate_Click(object sender, EventArgs e)
2030 InstallUpdateSyncWithInfo();
2038 if (!iDisplay.IsOpen())
2043 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
2044 iSkipFrameRendering = true;
2047 iDisplay.SwapBuffers();
2048 //Then show our clock
2049 iDisplay.ShowClock();
2057 if (!iDisplay.IsOpen())
2062 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
2063 iSkipFrameRendering = false;
2064 iDisplay.HideClock();
2067 private void InstallUpdateSyncWithInfo()
2069 UpdateCheckInfo info = null;
2071 if (ApplicationDeployment.IsNetworkDeployed)
2073 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
2077 info = ad.CheckForDetailedUpdate();
2080 catch (DeploymentDownloadException dde)
2082 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);
2085 catch (InvalidDeploymentException ide)
2087 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);
2090 catch (InvalidOperationException ioe)
2092 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
2096 if (info.UpdateAvailable)
2098 Boolean doUpdate = true;
2100 if (!info.IsUpdateRequired)
2102 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
2103 if (!(DialogResult.OK == dr))
2110 // Display a message that the app MUST reboot. Display the minimum required version.
2111 MessageBox.Show("This application has detected a mandatory update from your current " +
2112 "version to version " + info.MinimumRequiredVersion.ToString() +
2113 ". The application will now install the update and restart.",
2114 "Update Available", MessageBoxButtons.OK,
2115 MessageBoxIcon.Information);
2123 MessageBox.Show("The application has been upgraded, and will now restart.");
2124 Application.Restart();
2126 catch (DeploymentDownloadException dde)
2128 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
2135 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
2144 private void SysTrayHideShow()
2150 WindowState = FormWindowState.Normal;
2155 /// Use to handle minimize events.
2157 /// <param name="sender"></param>
2158 /// <param name="e"></param>
2159 private void MainForm_SizeChanged(object sender, EventArgs e)
2161 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2173 /// <param name="sender"></param>
2174 /// <param name="e"></param>
2175 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2177 //Our table layout size has changed which means our display size has changed.
2178 //We need to re-create our bitmap.
2179 iCreateBitmap = true;
2185 /// <param name="sender"></param>
2186 /// <param name="e"></param>
2187 private void buttonSelectFile_Click(object sender, EventArgs e)
2189 //openFileDialog1.InitialDirectory = "c:\\";
2190 //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
2191 //openFileDialog.FilterIndex = 1;
2192 openFileDialog.RestoreDirectory = true;
2194 if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
2196 labelStartFileName.Text = openFileDialog.FileName;
2197 Properties.Settings.Default.StartFileName = openFileDialog.FileName;
2198 Properties.Settings.Default.Save();
2205 /// <param name="sender"></param>
2206 /// <param name="e"></param>
2207 private void comboBoxOpticalDrives_SelectedIndexChanged(object sender, EventArgs e)
2209 //Save the optical drive the user selected for ejection
2210 Properties.Settings.Default.OpticalDriveToEject = comboBoxOpticalDrives.SelectedItem.ToString();
2211 Properties.Settings.Default.Save();
2215 /// Broadcast messages to subscribers.
2217 /// <param name="message"></param>
2218 protected override void WndProc(ref Message aMessage)
2220 if (OnWndProc!=null)
2222 OnWndProc(ref aMessage);
2225 base.WndProc(ref aMessage);
2228 private void checkBoxCecEnabled_CheckedChanged(object sender, EventArgs e)
2230 //Save CEC enabled status
2231 Properties.Settings.Default.CecEnabled = checkBoxCecEnabled.Checked;
2232 Properties.Settings.Default.Save();
2237 private void comboBoxHdmiPort_SelectedIndexChanged(object sender, EventArgs e)
2239 //Save CEC HDMI port
2240 Properties.Settings.Default.CecHdmiPort = Convert.ToByte(comboBoxHdmiPort.SelectedIndex);
2241 Properties.Settings.Default.CecHdmiPort++;
2242 Properties.Settings.Default.Save();
2247 private void checkBoxCecMonitorOff_CheckedChanged(object sender, EventArgs e)
2249 Properties.Settings.Default.CecMonitorOff = checkBoxCecMonitorOff.Checked;
2250 Properties.Settings.Default.Save();
2255 private void checkBoxCecMonitorOn_CheckedChanged(object sender, EventArgs e)
2257 Properties.Settings.Default.CecMonitorOn = checkBoxCecMonitorOn.Checked;
2258 Properties.Settings.Default.Save();
2266 private void ResetCec()
2268 if (iCecManager==null)
2270 //Thus skipping initial UI setup
2276 if (Properties.Settings.Default.CecEnabled)
2278 iCecManager.Start(Handle, "CEC",
2279 Properties.Settings.Default.CecHdmiPort,
2280 Properties.Settings.Default.CecMonitorOn,
2281 Properties.Settings.Default.CecMonitorOff);