Persisting selection of optical drive to eject.
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();
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;
98 /// Manage run when Windows startup option
100 private StartupManager iStartupManager;
103 /// System tray icon.
105 private NotifyIconAdv iNotifyIcon;
109 iSkipFrameRendering = false;
111 iCurrentClientSessionId = "";
112 iCurrentClientData = null;
113 LastTickTime = DateTime.Now;
114 //Instantiate our display and register for events notifications
115 iDisplay = new Display();
116 iDisplay.OnOpened += OnDisplayOpened;
117 iDisplay.OnClosed += OnDisplayClosed;
119 iClients = new Dictionary<string, ClientData>();
120 iStartupManager = new StartupManager();
121 iNotifyIcon = new NotifyIconAdv();
123 //Have our designer initialize its controls
124 InitializeComponent();
126 //Populate device types
127 PopulateDeviceTypes();
129 //Populate optical drives
130 PopulateOpticalDrives();
132 //Initial status update
135 //We have a bug when drawing minimized and reusing our bitmap
136 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
137 iCreateBitmap = false;
139 //Minimize our window if desired
140 if (Properties.Settings.Default.StartMinimized)
142 WindowState = FormWindowState.Minimized;
150 /// <param name="sender"></param>
151 /// <param name="e"></param>
152 private void MainForm_Load(object sender, EventArgs e)
154 //Check if we are running a Click Once deployed application
155 if (ApplicationDeployment.IsNetworkDeployed)
157 //This is a proper Click Once installation, fetch and show our version number
158 this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion;
162 //Not a proper Click Once installation, assuming development build then
163 this.Text += " - development";
167 iMultiMediaDeviceEnumerator = new MMDeviceEnumerator();
168 iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this);
169 UpdateAudioDeviceAndMasterVolumeThreadSafe();
172 iNetworkManager = new NetworkManager();
173 iNetworkManager.OnConnectivityChanged += OnConnectivityChanged;
174 UpdateNetworkStatus();
176 //Setup notification icon
179 // To make sure start up with minimize to tray works
180 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
186 //When not debugging we want the screen to be empty until a client takes over
189 //When developing we want at least one client for testing
190 StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789");
193 //Open display connection on start-up if needed
194 if (Properties.Settings.Default.DisplayConnectOnStartup)
196 OpenDisplayConnection();
199 //Start our server so that we can get client requests
202 //Register for HID events
203 RegisterHidDevices();
207 /// Called when our display is opened.
209 /// <param name="aDisplay"></param>
210 private void OnDisplayOpened(Display aDisplay)
212 //Make sure we resume frame rendering
213 iSkipFrameRendering = false;
215 //Set our screen size now that our display is connected
216 //Our panelDisplay is the container of our tableLayoutPanel
217 //tableLayoutPanel will resize itself to fit the client size of our panelDisplay
218 //panelDisplay needs an extra 2 pixels for borders on each sides
219 //tableLayoutPanel will eventually be the exact size of our display
220 Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2);
221 panelDisplay.Size = size;
223 //Our display was just opened, update our UI
225 //Initiate asynchronous request
226 iDisplay.RequestFirmwareRevision();
229 UpdateMasterVolumeThreadSafe();
231 UpdateNetworkStatus();
234 //Testing icon in debug, no arm done if icon not supported
235 //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1);
236 //iDisplay.SetAllIconsStatus(2);
242 /// Called when our display is closed.
244 /// <param name="aDisplay"></param>
245 private void OnDisplayClosed(Display aDisplay)
247 //Our display was just closed, update our UI consequently
251 public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity)
253 //Update network status
254 UpdateNetworkStatus();
258 /// Update our Network Status
260 private void UpdateNetworkStatus()
262 if (iDisplay.IsOpen())
264 iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet, iNetworkManager.NetworkListManager.IsConnectedToInternet);
265 iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected);
270 int iLastNetworkIconIndex = 0;
271 int iUpdateCountSinceLastNetworkAnimation = 0;
276 private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime)
278 iUpdateCountSinceLastNetworkAnimation++;
279 iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4;
281 if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0)
283 int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal);
286 //Prevents div by zero and other undefined behavior
289 iLastNetworkIconIndex++;
290 iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2);
291 for (int i=0;i<iconCount;i++)
293 if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && !(i == 1 && iLastNetworkIconIndex > 4))
295 iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i);
299 iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i);
308 /// Receive volume change notification and reflect changes on our slider.
310 /// <param name="data"></param>
311 public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data)
313 UpdateMasterVolumeThreadSafe();
317 /// Update master volume when user moves our slider.
319 /// <param name="sender"></param>
320 /// <param name="e"></param>
321 private void trackBarMasterVolume_Scroll(object sender, EventArgs e)
323 //Just like Windows Volume Mixer we unmute if the volume is adjusted
324 iMultiMediaDevice.AudioEndpointVolume.Mute = false;
325 //Set volume level according to our volume slider new position
326 iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f;
331 /// Mute check box changed.
333 /// <param name="sender"></param>
334 /// <param name="e"></param>
335 private void checkBoxMute_CheckedChanged(object sender, EventArgs e)
337 iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked;
341 /// Device State Changed
343 public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){}
348 public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { }
353 public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { }
356 /// Default Device Changed
358 public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId)
360 if (role == Role.Multimedia && flow == DataFlow.Render)
362 UpdateAudioDeviceAndMasterVolumeThreadSafe();
367 /// Property Value Changed
369 /// <param name="pwstrDeviceId"></param>
370 /// <param name="key"></param>
371 public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){}
377 /// Update master volume indicators based our current system states.
378 /// This typically includes volume levels and mute status.
380 private void UpdateMasterVolumeThreadSafe()
382 if (this.InvokeRequired)
384 //Not in the proper thread, invoke ourselves
385 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe);
386 this.Invoke(d, new object[] { });
390 //Update volume slider
391 float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar;
392 trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100);
393 //Update mute checkbox
394 checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute;
396 //If our display connection is open we need to update its icons
397 if (iDisplay.IsOpen())
399 //First take care our our volume level icons
400 int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume);
401 if (volumeIconCount > 0)
403 //Compute current volume level from system level and the number of segments in our display volume bar.
404 //That tells us how many segments in our volume bar needs to be turned on.
405 float currentVolume = volumeLevelScalar * volumeIconCount;
406 int segmentOnCount = Convert.ToInt32(currentVolume);
407 //Check if our segment count was rounded up, this will later be used for half brightness segment
408 bool roundedUp = segmentOnCount > currentVolume;
410 for (int i = 0; i < volumeIconCount; i++)
412 if (i < segmentOnCount)
414 //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness.
415 if (i == segmentOnCount - 1 && roundedUp)
418 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1) / 2);
423 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1);
428 iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0);
433 //Take care our our mute icon
434 iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iMultiMediaDevice.AudioEndpointVolume.Mute);
442 private void UpdateAudioDeviceAndMasterVolumeThreadSafe()
444 if (this.InvokeRequired)
446 //Not in the proper thread, invoke ourselves
447 PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe);
448 this.Invoke(d, new object[] { });
452 //We are in the correct thread just go ahead.
455 //Get our master volume
456 iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
458 labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName;
460 //Show our volume in our track bar
461 UpdateMasterVolumeThreadSafe();
463 //Register to get volume modifications
464 iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe;
466 trackBarMasterVolume.Enabled = true;
470 Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume");
471 Debug.WriteLine(ex.ToString());
472 //Something went wrong S/PDIF device ca throw exception I guess
473 trackBarMasterVolume.Enabled = false;
480 private void PopulateDeviceTypes()
482 int count = Display.TypeCount();
484 for (int i = 0; i < count; i++)
486 comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type)i));
493 private void PopulateOpticalDrives()
495 //Reset our list of drives
496 comboBoxOpticalDrives.Items.Clear();
497 comboBoxOpticalDrives.Items.Add("None");
499 //Go through each drives on our system and collected the optical ones in our list
500 DriveInfo[] allDrives = DriveInfo.GetDrives();
501 foreach (DriveInfo d in allDrives)
503 Debug.WriteLine("Drive {0}", d.Name);
504 Debug.WriteLine(" Drive type: {0}", d.DriveType);
506 if (d.DriveType==DriveType.CDRom)
508 //This is an optical drive, add it now
509 comboBoxOpticalDrives.Items.Add(d.Name.Substring(0,2));
517 /// <returns></returns>
518 public string OpticalDriveToEject()
520 return comboBoxOpticalDrives.SelectedItem.ToString();
528 private void SetupTrayIcon()
530 iNotifyIcon.Icon = GetIcon("vfd.ico");
531 iNotifyIcon.Text = "Sharp Display Manager";
532 iNotifyIcon.Visible = true;
534 //Double click toggles visibility - typically brings up the application
535 iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args)
540 //Adding a context menu, useful to be able to exit the application
541 ContextMenu contextMenu = new ContextMenu();
542 //Context menu item to toggle visibility
543 MenuItem hideShowItem = new MenuItem("Hide/Show");
544 hideShowItem.Click += delegate(object obj, EventArgs args)
548 contextMenu.MenuItems.Add(hideShowItem);
550 //Context menu item separator
551 contextMenu.MenuItems.Add(new MenuItem("-"));
553 //Context menu exit item
554 MenuItem exitItem = new MenuItem("Exit");
555 exitItem.Click += delegate(object obj, EventArgs args)
559 contextMenu.MenuItems.Add(exitItem);
561 iNotifyIcon.ContextMenu = contextMenu;
565 /// Access icons from embedded resources.
567 /// <param name="name"></param>
568 /// <returns></returns>
569 public static Icon GetIcon(string name)
571 name = "SharpDisplayManager.Resources." + name;
574 Assembly.GetExecutingAssembly().GetManifestResourceNames();
575 for (int i = 0; i < names.Length; i++)
577 if (names[i].Replace('\\', '.') == name)
579 using (Stream stream = Assembly.GetExecutingAssembly().
580 GetManifestResourceStream(names[i]))
582 return new Icon(stream);
592 /// Set our current client.
593 /// This will take care of applying our client layout and set data fields.
595 /// <param name="aSessionId"></param>
596 void SetCurrentClient(string aSessionId, bool aForce=false)
598 if (aSessionId == iCurrentClientSessionId)
600 //Given client is already the current one.
601 //Don't bother changing anything then.
606 //Check when was the last time we switched to that client
607 if (iCurrentClientData != null)
609 double lastSwitchToClientSecondsAgo = (DateTime.Now - iCurrentClientData.LastSwitchTime).TotalSeconds;
610 //TODO: put that hard coded value as a client property
611 //Clients should be able to define how often they can be interrupted
612 //Thus a background client can set this to zero allowing any other client to interrupt at any time
613 //We could also compute this delay by looking at the requests frequencies?
614 if (!aForce && (lastSwitchToClientSecondsAgo < 30)) //Make sure a client is on for at least 30 seconds
616 //Don't switch clients too often
621 //Set current client ID.
622 iCurrentClientSessionId = aSessionId;
623 //Set the time we last switched to that client
624 iClients[aSessionId].LastSwitchTime = DateTime.Now;
625 //Fetch and set current client data.
626 iCurrentClientData = iClients[aSessionId];
627 //Apply layout and set data fields.
628 UpdateTableLayoutPanel(iCurrentClientData);
631 private void buttonFont_Click(object sender, EventArgs e)
633 //fontDialog.ShowColor = true;
634 //fontDialog.ShowApply = true;
635 fontDialog.ShowEffects = true;
636 fontDialog.Font = cds.Font;
638 fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked;
640 //fontDialog.ShowHelp = true;
642 //fontDlg.MaxSize = 40;
643 //fontDlg.MinSize = 22;
645 //fontDialog.Parent = this;
646 //fontDialog.StartPosition = FormStartPosition.CenterParent;
648 //DlgBox.ShowDialog(fontDialog);
650 //if (fontDialog.ShowDialog(this) != DialogResult.Cancel)
651 if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel)
653 //Set the fonts to all our labels in our layout
654 foreach (Control ctrl in tableLayoutPanel.Controls)
656 if (ctrl is MarqueeLabel)
658 ((MarqueeLabel)ctrl).Font = fontDialog.Font;
663 cds.Font = fontDialog.Font;
664 Properties.Settings.Default.Save();
673 void CheckFontHeight()
675 //Show font height and width
676 labelFontHeight.Text = "Font height: " + cds.Font.Height;
677 float charWidth = IsFixedWidth(cds.Font);
678 if (charWidth == 0.0f)
680 labelFontWidth.Visible = false;
684 labelFontWidth.Visible = true;
685 labelFontWidth.Text = "Font width: " + charWidth;
688 MarqueeLabel label = null;
689 //Get the first label control we can find
690 foreach (Control ctrl in tableLayoutPanel.Controls)
692 if (ctrl is MarqueeLabel)
694 label = (MarqueeLabel)ctrl;
699 //Now check font height and show a warning if needed.
700 if (label != null && label.Font.Height > label.Height)
702 labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!";
703 labelWarning.Visible = true;
707 labelWarning.Visible = false;
712 private void buttonCapture_Click(object sender, EventArgs e)
714 System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height);
715 tableLayoutPanel.DrawToBitmap(bmp, tableLayoutPanel.ClientRectangle);
716 //Bitmap bmpToSave = new Bitmap(bmp);
717 bmp.Save("D:\\capture.png");
719 ((MarqueeLabel)tableLayoutPanel.Controls[0]).Text = "Captured";
722 string outputFileName = "d:\\capture.png";
723 using (MemoryStream memory = new MemoryStream())
725 using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
727 bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png);
728 byte[] bytes = memory.ToArray();
729 fs.Write(bytes, 0, bytes.Length);
736 private void CheckForRequestResults()
738 if (iDisplay.IsRequestPending())
740 switch (iDisplay.AttemptRequestCompletion())
742 case MiniDisplay.Request.FirmwareRevision:
743 toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision();
744 //Issue next request then
745 iDisplay.RequestPowerSupplyStatus();
748 case MiniDisplay.Request.PowerSupplyStatus:
749 if (iDisplay.PowerSupplyStatus())
751 toolStripStatusLabelPower.Text = "ON";
755 toolStripStatusLabelPower.Text = "OFF";
757 //Issue next request then
758 iDisplay.RequestDeviceId();
761 case MiniDisplay.Request.DeviceId:
762 toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId();
763 //No more request to issue
769 public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel)
771 if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF)
778 public static uint ColorUntouched(int aX, int aY, uint aPixel)
783 public static uint ColorInversed(int aX, int aY, uint aPixel)
788 public static uint ColorChessboard(int aX, int aY, uint aPixel)
790 if ((aX % 2 == 0) && (aY % 2 == 0))
794 else if ((aX % 2 != 0) && (aY % 2 != 0))
802 public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX)
804 return aBmp.Width - aX - 1;
807 public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY)
809 return iBmp.Height - aY - 1;
812 public int ScreenX(System.Drawing.Bitmap aBmp, int aX)
817 public int ScreenY(System.Drawing.Bitmap aBmp, int aY)
823 /// Select proper pixel delegates according to our current settings.
825 private void SetupPixelDelegates()
827 //Select our pixel processing routine
828 if (cds.InverseColors)
830 //iColorFx = ColorChessboard;
831 iColorFx = ColorInversed;
835 iColorFx = ColorWhiteIsOn;
838 //Select proper coordinate translation functions
839 //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels
840 if (cds.ReverseScreen)
842 iScreenX = ScreenReversedX;
843 iScreenY = ScreenReversedY;
853 //This is our timer tick responsible to perform our render
854 private void timer_Tick(object sender, EventArgs e)
856 //Update our animations
857 DateTime NewTickTime = DateTime.Now;
859 UpdateNetworkSignal(LastTickTime, NewTickTime);
861 //Update animation for all our marquees
862 foreach (Control ctrl in tableLayoutPanel.Controls)
864 if (ctrl is MarqueeLabel)
866 ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime);
871 if (iDisplay.IsOpen())
873 CheckForRequestResults();
875 //Check if frame rendering is needed
876 //Typically used when showing clock
877 if (!iSkipFrameRendering)
882 iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
884 tableLayoutPanel.DrawToBitmap(iBmp, tableLayoutPanel.ClientRectangle);
885 //iBmp.Save("D:\\capture.png");
887 //Send it to our display
888 for (int i = 0; i < iBmp.Width; i++)
890 for (int j = 0; j < iBmp.Height; j++)
894 //Get our processed pixel coordinates
895 int x = iScreenX(iBmp, i);
896 int y = iScreenY(iBmp, j);
898 uint color = (uint)iBmp.GetPixel(i, j).ToArgb();
899 //Apply color effects
900 color = iColorFx(x, y, color);
902 iDisplay.SetPixel(x, y, color);
907 iDisplay.SwapBuffers();
911 //Compute instant FPS
912 toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS";
914 LastTickTime = NewTickTime;
919 /// Attempt to establish connection with our display hardware.
921 private void OpenDisplayConnection()
923 CloseDisplayConnection();
925 if (!iDisplay.Open((MiniDisplay.Type)cds.DisplayType))
928 toolStripStatusLabelConnect.Text = "Connection error";
932 private void CloseDisplayConnection()
934 //Status will be updated upon receiving the closed event
936 if (iDisplay == null || !iDisplay.IsOpen())
941 //Do not clear if we gave up on rendering already.
942 //This means we will keep on displaying clock on MDM166AA for instance.
943 if (!iSkipFrameRendering)
946 iDisplay.SwapBuffers();
949 iDisplay.SetAllIconsStatus(0); //Turn off all icons
953 private void buttonOpen_Click(object sender, EventArgs e)
955 OpenDisplayConnection();
958 private void buttonClose_Click(object sender, EventArgs e)
960 CloseDisplayConnection();
963 private void buttonClear_Click(object sender, EventArgs e)
966 iDisplay.SwapBuffers();
969 private void buttonFill_Click(object sender, EventArgs e)
972 iDisplay.SwapBuffers();
975 private void trackBarBrightness_Scroll(object sender, EventArgs e)
977 cds.Brightness = trackBarBrightness.Value;
978 Properties.Settings.Default.Save();
979 iDisplay.SetBrightness(trackBarBrightness.Value);
985 /// CDS stands for Current Display Settings
987 private DisplaySettings cds
991 DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings;
992 if (settings == null)
994 settings = new DisplaysSettings();
996 Properties.Settings.Default.DisplaysSettings = settings;
999 //Make sure all our settings have been created
1000 while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex)
1002 settings.Displays.Add(new DisplaySettings());
1005 DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex];
1006 return displaySettings;
1011 /// Check if the given font has a fixed character pitch.
1013 /// <param name="ft"></param>
1014 /// <returns>0.0f if this is not a monospace font, otherwise returns the character width.</returns>
1015 public float IsFixedWidth(Font ft)
1017 Graphics g = CreateGraphics();
1018 char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' };
1019 float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width;
1021 bool fixedWidth = true;
1023 foreach (char c in charSizes)
1024 if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth)
1036 /// Synchronize UI with settings
1038 private void UpdateStatus()
1041 checkBoxShowBorders.Checked = cds.ShowBorders;
1042 tableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1044 //Set the proper font to each of our labels
1045 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1047 ctrl.Font = cds.Font;
1051 //Check if "run on Windows startup" is enabled
1052 checkBoxAutoStart.Checked = iStartupManager.Startup;
1054 checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup;
1055 checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray;
1056 checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized;
1057 labelStartFileName.Text = Properties.Settings.Default.StartFileName;
1059 //Try find our drive in our drive list
1060 int opticalDriveItemIndex=0;
1061 bool driveNotFound = true;
1062 string opticalDriveToEject=Properties.Settings.Default.OpticalDriveToEject;
1063 foreach (object item in comboBoxOpticalDrives.Items)
1065 if (opticalDriveToEject.Equals(item.ToString()))
1067 comboBoxOpticalDrives.SelectedIndex = opticalDriveItemIndex;
1068 driveNotFound = false;
1071 opticalDriveItemIndex++;
1076 //We could not find the drive we had saved.
1077 //Select "None" then.
1078 comboBoxOpticalDrives.SelectedIndex = 0;
1083 checkBoxReverseScreen.Checked = cds.ReverseScreen;
1084 checkBoxInverseColors.Checked = cds.InverseColors;
1085 checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel;
1086 checkBoxScaleToFit.Checked = cds.ScaleToFit;
1087 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1088 labelMinFontSize.Enabled = cds.ScaleToFit;
1089 maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString();
1090 maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString();
1091 comboBoxDisplayType.SelectedIndex = cds.DisplayType;
1092 timer.Interval = cds.TimerInterval;
1093 maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString();
1094 textBoxScrollLoopSeparator.Text = cds.Separator;
1096 SetupPixelDelegates();
1098 if (iDisplay.IsOpen())
1100 //We have a display connection
1101 //Reflect that in our UI
1103 tableLayoutPanel.Enabled = true;
1104 panelDisplay.Enabled = true;
1106 //Only setup brightness if display is open
1107 trackBarBrightness.Minimum = iDisplay.MinBrightness();
1108 trackBarBrightness.Maximum = iDisplay.MaxBrightness();
1109 if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness())
1111 //Brightness out of range, this can occur when using auto-detect
1112 //Use max brightness instead
1113 trackBarBrightness.Value = iDisplay.MaxBrightness();
1114 iDisplay.SetBrightness(iDisplay.MaxBrightness());
1118 trackBarBrightness.Value = cds.Brightness;
1119 iDisplay.SetBrightness(cds.Brightness);
1122 //Try compute the steps to something that makes sense
1123 trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5);
1124 trackBarBrightness.SmallChange = 1;
1127 buttonFill.Enabled = true;
1128 buttonClear.Enabled = true;
1129 buttonOpen.Enabled = false;
1130 buttonClose.Enabled = true;
1131 trackBarBrightness.Enabled = true;
1132 toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product();
1133 //+ " - " + iDisplay.SerialNumber();
1135 if (iDisplay.SupportPowerOnOff())
1137 buttonPowerOn.Enabled = true;
1138 buttonPowerOff.Enabled = true;
1142 buttonPowerOn.Enabled = false;
1143 buttonPowerOff.Enabled = false;
1146 if (iDisplay.SupportClock())
1148 buttonShowClock.Enabled = true;
1149 buttonHideClock.Enabled = true;
1153 buttonShowClock.Enabled = false;
1154 buttonHideClock.Enabled = false;
1158 //Check if Volume Label is supported. To date only MDM166AA supports that crap :)
1159 checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel)>0;
1161 if (cds.ShowVolumeLabel)
1163 iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel);
1167 iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel);
1172 //Display is connection not available
1173 //Reflect that in our UI
1174 checkBoxShowVolumeLabel.Enabled = false;
1175 tableLayoutPanel.Enabled = false;
1176 panelDisplay.Enabled = false;
1177 buttonFill.Enabled = false;
1178 buttonClear.Enabled = false;
1179 buttonOpen.Enabled = true;
1180 buttonClose.Enabled = false;
1181 trackBarBrightness.Enabled = false;
1182 buttonPowerOn.Enabled = false;
1183 buttonPowerOff.Enabled = false;
1184 buttonShowClock.Enabled = false;
1185 buttonHideClock.Enabled = false;
1186 toolStripStatusLabelConnect.Text = "Disconnected";
1187 toolStripStatusLabelPower.Text = "N/A";
1196 /// <param name="sender"></param>
1197 /// <param name="e"></param>
1198 private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e)
1200 cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked;
1201 Properties.Settings.Default.Save();
1205 private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e)
1207 //Save our show borders setting
1208 tableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None);
1209 cds.ShowBorders = checkBoxShowBorders.Checked;
1210 Properties.Settings.Default.Save();
1214 private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e)
1216 //Save our connect on startup setting
1217 Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked;
1218 Properties.Settings.Default.Save();
1221 private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e)
1223 //Save our "Minimize to tray" setting
1224 Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked;
1225 Properties.Settings.Default.Save();
1229 private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e)
1231 //Save our "Start minimized" setting
1232 Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked;
1233 Properties.Settings.Default.Save();
1236 private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e)
1238 iStartupManager.Startup = checkBoxAutoStart.Checked;
1242 private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e)
1244 //Save our reverse screen setting
1245 cds.ReverseScreen = checkBoxReverseScreen.Checked;
1246 Properties.Settings.Default.Save();
1247 SetupPixelDelegates();
1250 private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e)
1252 //Save our inverse colors setting
1253 cds.InverseColors = checkBoxInverseColors.Checked;
1254 Properties.Settings.Default.Save();
1255 SetupPixelDelegates();
1258 private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e)
1260 //Save our scale to fit setting
1261 cds.ScaleToFit = checkBoxScaleToFit.Checked;
1262 Properties.Settings.Default.Save();
1264 labelMinFontSize.Enabled = cds.ScaleToFit;
1265 maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit;
1268 private void MainForm_Resize(object sender, EventArgs e)
1270 if (WindowState == FormWindowState.Minimized)
1273 //iBmp = new System.Drawing.Bitmap(tableLayoutPanel.Width, tableLayoutPanel.Height, PixelFormat.Format32bppArgb);
1274 iCreateBitmap = true;
1278 private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
1280 iNetworkManager.Dispose();
1281 CloseDisplayConnection();
1283 e.Cancel = iClosing;
1286 public void StartServer()
1288 iServiceHost = new ServiceHost
1291 new Uri[] { new Uri("net.tcp://localhost:8001/") }
1294 iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService");
1295 iServiceHost.Open();
1298 public void StopServer()
1300 if (iClients.Count > 0 && !iClosing)
1304 BroadcastCloseEvent();
1308 if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
1310 iClosing = false; //We make sure we force close if asked twice
1315 //We removed that as it often lags for some reason
1316 //iServiceHost.Close();
1320 public void BroadcastCloseEvent()
1322 Trace.TraceInformation("BroadcastCloseEvent - start");
1324 var inactiveClients = new List<string>();
1325 foreach (var client in iClients)
1327 //if (client.Key != eventData.ClientName)
1331 Trace.TraceInformation("BroadcastCloseEvent - " + client.Key);
1332 client.Value.Callback.OnCloseOrder(/*eventData*/);
1334 catch (Exception ex)
1336 inactiveClients.Add(client.Key);
1341 if (inactiveClients.Count > 0)
1343 foreach (var client in inactiveClients)
1345 iClients.Remove(client);
1346 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(client, false)[0]);
1350 if (iClients.Count==0)
1357 /// Just remove all our fields.
1359 private void ClearLayout()
1361 tableLayoutPanel.Controls.Clear();
1362 tableLayoutPanel.RowStyles.Clear();
1363 tableLayoutPanel.ColumnStyles.Clear();
1364 iCurrentClientData = null;
1368 /// Just launch a demo client.
1370 private void StartNewClient(string aTopText = "", string aBottomText = "")
1372 Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams);
1373 SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText);
1374 clientThread.Start(myParams);
1378 private void buttonStartClient_Click(object sender, EventArgs e)
1383 private void buttonSuspend_Click(object sender, EventArgs e)
1385 LastTickTime = DateTime.Now; //Reset timer to prevent jump
1386 timer.Enabled = !timer.Enabled;
1389 buttonSuspend.Text = "Run";
1393 buttonSuspend.Text = "Pause";
1397 private void buttonCloseClients_Click(object sender, EventArgs e)
1399 BroadcastCloseEvent();
1402 private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e)
1404 //Root node must have at least one child
1405 if (e.Node.Nodes.Count == 0)
1410 //If the selected node is the root node of a client then switch to it
1411 string sessionId=e.Node.Nodes[0].Text; //First child of a root node is the sessionId
1412 if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at
1414 //We have a valid session just switch to that client
1415 SetCurrentClient(sessionId,true);
1424 /// <param name="aSessionId"></param>
1425 /// <param name="aCallback"></param>
1426 public void AddClientThreadSafe(string aSessionId, ICallback aCallback)
1428 if (this.InvokeRequired)
1430 //Not in the proper thread, invoke ourselves
1431 AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe);
1432 this.Invoke(d, new object[] { aSessionId, aCallback });
1436 //We are in the proper thread
1437 //Add this session to our collection of clients
1438 ClientData newClient = new ClientData(aSessionId, aCallback);
1439 Program.iMainForm.iClients.Add(aSessionId, newClient);
1440 //Add this session to our UI
1441 UpdateClientTreeViewNode(newClient);
1448 /// <param name="aSessionId"></param>
1449 public void RemoveClientThreadSafe(string aSessionId)
1451 if (this.InvokeRequired)
1453 //Not in the proper thread, invoke ourselves
1454 RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe);
1455 this.Invoke(d, new object[] { aSessionId });
1459 //We are in the proper thread
1460 //Remove this session from both client collection and UI tree view
1461 if (Program.iMainForm.iClients.Keys.Contains(aSessionId))
1463 Program.iMainForm.iClients.Remove(aSessionId);
1464 Program.iMainForm.treeViewClients.Nodes.Remove(Program.iMainForm.treeViewClients.Nodes.Find(aSessionId, false)[0]);
1467 if (iClients.Count == 0)
1469 //Clear our screen when last client disconnects
1474 //We were closing our form
1475 //All clients are now closed
1476 //Just resume our close operation
1487 /// <param name="aSessionId"></param>
1488 /// <param name="aLayout"></param>
1489 public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout)
1491 if (this.InvokeRequired)
1493 //Not in the proper thread, invoke ourselves
1494 SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe);
1495 this.Invoke(d, new object[] { aSessionId, aLayout });
1499 ClientData client = iClients[aSessionId];
1502 //Don't change a thing if the layout is the same
1503 if (!client.Layout.IsSameAs(aLayout))
1505 //Set our client layout then
1506 client.Layout = aLayout;
1508 UpdateClientTreeViewNode(client);
1517 /// <param name="aSessionId"></param>
1518 /// <param name="aField"></param>
1519 public void SetClientFieldThreadSafe(string aSessionId, DataField aField)
1521 if (this.InvokeRequired)
1523 //Not in the proper thread, invoke ourselves
1524 SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe);
1525 this.Invoke(d, new object[] { aSessionId, aField });
1529 //We are in the proper thread
1530 //Call the non-thread-safe variant
1531 SetClientField(aSessionId, aField);
1538 /// <param name="aSessionId"></param>
1539 /// <param name="aField"></param>
1540 private void SetClientField(string aSessionId, DataField aField)
1542 //TODO: should check if the field actually changed?
1544 ClientData client = iClients[aSessionId];
1545 bool layoutChanged = false;
1546 bool contentChanged = true;
1548 //Make sure all our fields are in place
1549 while (client.Fields.Count < (aField.Index + 1))
1551 //Add a data field with proper index
1552 client.Fields.Add(new DataField(client.Fields.Count));
1553 layoutChanged = true;
1556 //Now that we know hour fields are in place just update that one
1557 client.Fields[aField.Index] = aField;
1559 if (client.Fields[aField.Index].IsSameLayout(aField))
1561 //If we are updating a field in our current client we need to update it in our panel
1562 if (aSessionId == iCurrentClientSessionId)
1564 if (aField.IsText && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is MarqueeLabel)
1566 //Text field control already in place, just change the text
1567 MarqueeLabel label = (MarqueeLabel)tableLayoutPanel.Controls[aField.Index];
1568 contentChanged = (label.Text != aField.Text || label.TextAlign != aField.Alignment);
1569 label.Text = aField.Text;
1570 label.TextAlign = aField.Alignment;
1572 else if (aField.IsBitmap && aField.Index < tableLayoutPanel.Controls.Count && tableLayoutPanel.Controls[aField.Index] is PictureBox)
1574 contentChanged = true; //TODO: Bitmap comp or should we leave that to clients?
1575 //Bitmap field control already in place just change the bitmap
1576 PictureBox pictureBox = (PictureBox)tableLayoutPanel.Controls[aField.Index];
1577 pictureBox.Image = aField.Bitmap;
1581 layoutChanged = true;
1587 layoutChanged = true;
1590 //If either content or layout changed we need to update our tree view to reflect the changes
1591 if (contentChanged || layoutChanged)
1593 UpdateClientTreeViewNode(client);
1597 Debug.Print("Layout changed");
1598 //Our layout has changed, if we are already the current client we need to update our panel
1599 if (aSessionId == iCurrentClientSessionId)
1601 //Apply layout and set data fields.
1602 UpdateTableLayoutPanel(iCurrentClientData);
1607 //When a client field is set we try switching to this client to present the new information to our user
1608 SetCurrentClient(aSessionId);
1614 /// <param name="aTexts"></param>
1615 public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList<DataField> aFields)
1617 if (this.InvokeRequired)
1619 //Not in the proper thread, invoke ourselves
1620 SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe);
1621 this.Invoke(d, new object[] { aSessionId, aFields });
1625 //Put each our text fields in a label control
1626 foreach (DataField field in aFields)
1628 SetClientField(aSessionId, field);
1636 /// <param name="aSessionId"></param>
1637 /// <param name="aName"></param>
1638 public void SetClientNameThreadSafe(string aSessionId, string aName)
1640 if (this.InvokeRequired)
1642 //Not in the proper thread, invoke ourselves
1643 SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe);
1644 this.Invoke(d, new object[] { aSessionId, aName });
1648 //We are in the proper thread
1650 ClientData client = iClients[aSessionId];
1654 client.Name = aName;
1655 //Update our tree-view
1656 UpdateClientTreeViewNode(client);
1664 /// <param name="aClient"></param>
1665 private void UpdateClientTreeViewNode(ClientData aClient)
1667 Debug.Print("UpdateClientTreeViewNode");
1669 if (aClient == null)
1674 TreeNode node = null;
1675 //Check that our client node already exists
1676 //Get our client root node using its key which is our session ID
1677 TreeNode[] nodes = treeViewClients.Nodes.Find(aClient.SessionId, false);
1678 if (nodes.Count()>0)
1680 //We already have a node for that client
1682 //Clear children as we are going to recreate them below
1687 //Client node does not exists create a new one
1688 treeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId);
1689 node = treeViewClients.Nodes.Find(aClient.SessionId, false)[0];
1695 if (aClient.Name != "")
1697 //We have a name, us it as text for our root node
1698 node.Text = aClient.Name;
1699 //Add a child with SessionId
1700 node.Nodes.Add(new TreeNode(aClient.SessionId));
1704 //No name, use session ID instead
1705 node.Text = aClient.SessionId;
1708 if (aClient.Fields.Count > 0)
1710 //Create root node for our texts
1711 TreeNode textsRoot = new TreeNode("Fields");
1712 node.Nodes.Add(textsRoot);
1713 //For each text add a new entry
1714 foreach (DataField field in aClient.Fields)
1716 if (!field.IsBitmap)
1718 DataField textField = (DataField)field;
1719 textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text));
1723 textsRoot.Nodes.Add(new TreeNode("[Bitmap]"));
1733 /// Update our table layout row styles to make sure each rows have similar height
1735 private void UpdateTableLayoutRowStyles()
1737 foreach (RowStyle rowStyle in tableLayoutPanel.RowStyles)
1739 rowStyle.SizeType = SizeType.Percent;
1740 rowStyle.Height = 100 / tableLayoutPanel.RowCount;
1745 /// Update our display table layout.
1747 /// <param name="aLayout"></param>
1748 private void UpdateTableLayoutPanel(ClientData aClient)
1750 Debug.Print("UpdateClientTreeViewNode");
1752 if (aClient == null)
1759 TableLayout layout = aClient.Layout;
1762 //First clean our current panel
1763 tableLayoutPanel.Controls.Clear();
1764 tableLayoutPanel.RowStyles.Clear();
1765 tableLayoutPanel.ColumnStyles.Clear();
1766 tableLayoutPanel.RowCount = 0;
1767 tableLayoutPanel.ColumnCount = 0;
1769 while (tableLayoutPanel.RowCount < layout.Rows.Count)
1771 tableLayoutPanel.RowCount++;
1774 while (tableLayoutPanel.ColumnCount < layout.Columns.Count)
1776 tableLayoutPanel.ColumnCount++;
1779 for (int i = 0; i < tableLayoutPanel.ColumnCount; i++)
1781 //Create our column styles
1782 this.tableLayoutPanel.ColumnStyles.Add(layout.Columns[i]);
1784 for (int j = 0; j < tableLayoutPanel.RowCount; j++)
1788 //Create our row styles
1789 this.tableLayoutPanel.RowStyles.Add(layout.Rows[j]);
1792 //Check if we already have a control
1793 Control existingControl = tableLayoutPanel.GetControlFromPosition(i,j);
1794 if (existingControl!=null)
1796 //We already have a control in that cell as a results of row/col spanning
1797 //Move on to next cell then
1803 //Check if a client field already exists for that cell
1804 if (aClient.Fields.Count <= tableLayoutPanel.Controls.Count)
1806 //No client field specified, create a text field by default
1807 aClient.Fields.Add(new DataField(aClient.Fields.Count));
1810 //Create a control corresponding to the field specified for that cell
1811 DataField field = aClient.Fields[tableLayoutPanel.Controls.Count];
1812 Control control = CreateControlForDataField(field);
1814 //Add newly created control to our table layout at the specified row and column
1815 tableLayoutPanel.Controls.Add(control, i, j);
1816 //Make sure we specify row and column span for that new control
1817 tableLayoutPanel.SetRowSpan(control,field.RowSpan);
1818 tableLayoutPanel.SetColumnSpan(control, field.ColumnSpan);
1823 while (aClient.Fields.Count > fieldCount)
1825 //We have too much fields for this layout
1826 //Just discard them until we get there
1827 aClient.Fields.RemoveAt(aClient.Fields.Count-1);
1834 /// Check our type of data field and create corresponding control
1836 /// <param name="aField"></param>
1837 private Control CreateControlForDataField(DataField aField)
1839 Control control=null;
1840 if (!aField.IsBitmap)
1842 MarqueeLabel label = new SharpDisplayManager.MarqueeLabel();
1843 label.AutoEllipsis = true;
1844 label.AutoSize = true;
1845 label.BackColor = System.Drawing.Color.Transparent;
1846 label.Dock = System.Windows.Forms.DockStyle.Fill;
1847 label.Location = new System.Drawing.Point(1, 1);
1848 label.Margin = new System.Windows.Forms.Padding(0);
1849 label.Name = "marqueeLabel" + aField.Index;
1850 label.OwnTimer = false;
1851 label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond;
1852 label.Separator = cds.Separator;
1853 label.MinFontSize = cds.MinFontSize;
1854 label.ScaleToFit = cds.ScaleToFit;
1855 //control.Size = new System.Drawing.Size(254, 30);
1856 //control.TabIndex = 2;
1857 label.Font = cds.Font;
1859 label.TextAlign = aField.Alignment;
1860 label.UseCompatibleTextRendering = true;
1861 label.Text = aField.Text;
1867 //Create picture box
1868 PictureBox picture = new PictureBox();
1869 picture.AutoSize = true;
1870 picture.BackColor = System.Drawing.Color.Transparent;
1871 picture.Dock = System.Windows.Forms.DockStyle.Fill;
1872 picture.Location = new System.Drawing.Point(1, 1);
1873 picture.Margin = new System.Windows.Forms.Padding(0);
1874 picture.Name = "pictureBox" + aField;
1876 picture.Image = aField.Bitmap;
1885 /// Called when the user selected a new display type.
1887 /// <param name="sender"></param>
1888 /// <param name="e"></param>
1889 private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e)
1891 //Store the selected display type in our settings
1892 Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex;
1893 cds.DisplayType = comboBoxDisplayType.SelectedIndex;
1894 Properties.Settings.Default.Save();
1896 //Try re-opening the display connection if we were already connected.
1897 //Otherwise just update our status to reflect display type change.
1898 if (iDisplay.IsOpen())
1900 OpenDisplayConnection();
1908 private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e)
1910 if (maskedTextBoxTimerInterval.Text != "")
1912 int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text);
1916 timer.Interval = interval;
1917 cds.TimerInterval = timer.Interval;
1918 Properties.Settings.Default.Save();
1923 private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e)
1925 if (maskedTextBoxMinFontSize.Text != "")
1927 int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text);
1929 if (minFontSize > 0)
1931 cds.MinFontSize = minFontSize;
1932 Properties.Settings.Default.Save();
1933 //We need to recreate our layout for that change to take effect
1934 UpdateTableLayoutPanel(iCurrentClientData);
1940 private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e)
1942 if (maskedTextBoxScrollingSpeed.Text != "")
1944 int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text);
1946 if (scrollingSpeed > 0)
1948 cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed;
1949 Properties.Settings.Default.Save();
1950 //We need to recreate our layout for that change to take effect
1951 UpdateTableLayoutPanel(iCurrentClientData);
1956 private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e)
1958 cds.Separator = textBoxScrollLoopSeparator.Text;
1959 Properties.Settings.Default.Save();
1961 //Update our text fields
1962 foreach (MarqueeLabel ctrl in tableLayoutPanel.Controls)
1964 ctrl.Separator = cds.Separator;
1969 private void buttonPowerOn_Click(object sender, EventArgs e)
1974 private void buttonPowerOff_Click(object sender, EventArgs e)
1976 iDisplay.PowerOff();
1979 private void buttonShowClock_Click(object sender, EventArgs e)
1984 private void buttonHideClock_Click(object sender, EventArgs e)
1989 private void buttonUpdate_Click(object sender, EventArgs e)
1991 InstallUpdateSyncWithInfo();
1999 if (!iDisplay.IsOpen())
2004 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
2005 iSkipFrameRendering = true;
2008 iDisplay.SwapBuffers();
2009 //Then show our clock
2010 iDisplay.ShowClock();
2018 if (!iDisplay.IsOpen())
2023 //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock
2024 iSkipFrameRendering = false;
2025 iDisplay.HideClock();
2028 private void InstallUpdateSyncWithInfo()
2030 UpdateCheckInfo info = null;
2032 if (ApplicationDeployment.IsNetworkDeployed)
2034 ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
2038 info = ad.CheckForDetailedUpdate();
2041 catch (DeploymentDownloadException dde)
2043 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);
2046 catch (InvalidDeploymentException ide)
2048 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);
2051 catch (InvalidOperationException ioe)
2053 MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
2057 if (info.UpdateAvailable)
2059 Boolean doUpdate = true;
2061 if (!info.IsUpdateRequired)
2063 DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel);
2064 if (!(DialogResult.OK == dr))
2071 // Display a message that the app MUST reboot. Display the minimum required version.
2072 MessageBox.Show("This application has detected a mandatory update from your current " +
2073 "version to version " + info.MinimumRequiredVersion.ToString() +
2074 ". The application will now install the update and restart.",
2075 "Update Available", MessageBoxButtons.OK,
2076 MessageBoxIcon.Information);
2084 MessageBox.Show("The application has been upgraded, and will now restart.");
2085 Application.Restart();
2087 catch (DeploymentDownloadException dde)
2089 MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
2096 MessageBox.Show("You are already running the latest version.", "Application up-to-date");
2105 private void SysTrayHideShow()
2111 WindowState = FormWindowState.Normal;
2116 /// Use to handle minimize events.
2118 /// <param name="sender"></param>
2119 /// <param name="e"></param>
2120 private void MainForm_SizeChanged(object sender, EventArgs e)
2122 if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray)
2134 /// <param name="sender"></param>
2135 /// <param name="e"></param>
2136 private void tableLayoutPanel_SizeChanged(object sender, EventArgs e)
2138 //Our table layout size has changed which means our display size has changed.
2139 //We need to re-create our bitmap.
2140 iCreateBitmap = true;
2146 /// <param name="sender"></param>
2147 /// <param name="e"></param>
2148 private void buttonSelectFile_Click(object sender, EventArgs e)
2150 //openFileDialog1.InitialDirectory = "c:\\";
2151 //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*";
2152 //openFileDialog.FilterIndex = 1;
2153 openFileDialog.RestoreDirectory = true;
2155 if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK)
2157 labelStartFileName.Text = openFileDialog.FileName;
2158 Properties.Settings.Default.StartFileName = openFileDialog.FileName;
2159 Properties.Settings.Default.Save();
2166 /// <param name="sender"></param>
2167 /// <param name="e"></param>
2168 private void comboBoxOpticalDrives_SelectedIndexChanged(object sender, EventArgs e)
2170 //Save the optical drive the user selected for ejection
2171 Properties.Settings.Default.OpticalDriveToEject = comboBoxOpticalDrives.SelectedItem.ToString();
2172 Properties.Settings.Default.Save();