StephaneLenclud@123: // StephaneLenclud@123: // Copyright (C) 2014-2015 Stéphane Lenclud. StephaneLenclud@123: // StephaneLenclud@123: // This file is part of SharpDisplayManager. StephaneLenclud@123: // StephaneLenclud@123: // SharpDisplayManager is free software: you can redistribute it and/or modify StephaneLenclud@123: // it under the terms of the GNU General Public License as published by StephaneLenclud@123: // the Free Software Foundation, either version 3 of the License, or StephaneLenclud@123: // (at your option) any later version. StephaneLenclud@123: // StephaneLenclud@123: // SharpDisplayManager is distributed in the hope that it will be useful, StephaneLenclud@123: // but WITHOUT ANY WARRANTY; without even the implied warranty of StephaneLenclud@123: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the StephaneLenclud@123: // GNU General Public License for more details. StephaneLenclud@123: // StephaneLenclud@123: // You should have received a copy of the GNU General Public License StephaneLenclud@123: // along with SharpDisplayManager. If not, see . StephaneLenclud@123: // StephaneLenclud@123: StephaneLenclud@123: using System; sl@0: using System.Collections.Generic; sl@0: using System.ComponentModel; sl@0: using System.Data; sl@0: using System.Drawing; sl@0: using System.Linq; sl@0: using System.Text; sl@0: using System.Threading.Tasks; sl@0: using System.Windows.Forms; sl@14: using System.IO; sl@0: using CodeProject.Dialog; sl@14: using System.Drawing.Imaging; sl@17: using System.ServiceModel; sl@25: using System.Threading; sl@31: using System.Diagnostics; sl@88: using System.Deployment.Application; sl@94: using System.Reflection; StephaneLenclud@112: //NAudio StephaneLenclud@112: using NAudio.CoreAudioApi; StephaneLenclud@112: using NAudio.CoreAudioApi.Interfaces; StephaneLenclud@112: using System.Runtime.InteropServices; StephaneLenclud@206: using CecSharp; StephaneLenclud@117: //Network StephaneLenclud@117: using NETWORKLIST; sl@25: // sl@25: using SharpDisplayClient; sl@55: using SharpDisplay; StephaneLenclud@135: using MiniDisplayInterop; StephaneLenclud@171: using SharpLib.Display; StephaneLenclud@210: using SharpLib.Ear; StephaneLenclud@124: sl@0: namespace SharpDisplayManager sl@0: { sl@58: //Types declarations sl@58: public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel); sl@58: public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt); sl@62: //Delegates are used for our thread safe method sl@62: public delegate void AddClientDelegate(string aSessionId, ICallback aCallback); sl@62: public delegate void RemoveClientDelegate(string aSessionId); sl@79: public delegate void SetFieldDelegate(string SessionId, DataField aField); sl@79: public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList aFields); sl@62: public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout); sl@62: public delegate void SetClientNameDelegate(string aSessionId, string aName); StephaneLenclud@184: public delegate void SetClientPriorityDelegate(string aSessionId, uint aPriority); StephaneLenclud@184: public delegate void PlainUpdateDelegate(); StephaneLenclud@167: public delegate void WndProcDelegate(ref Message aMessage); sl@58: sl@58: /// sl@58: /// Our Display manager main form sl@58: /// StephaneLenclud@126: [System.ComponentModel.DesignerCategory("Form")] StephaneLenclud@126: public partial class MainForm : MainFormHid, IMMNotificationClient sl@0: { Stephane@212: //public ManagerEventAction iManager = new ManagerEventAction(); sl@2: DateTime LastTickTime; sl@3: Display iDisplay; sl@14: System.Drawing.Bitmap iBmp; sl@14: bool iCreateBitmap; //Workaround render to bitmap issues when minimized sl@17: ServiceHost iServiceHost; sl@65: // Our collection of clients sorted by session id. sl@33: public Dictionary iClients; sl@65: // The name of the client which informations are currently displayed. sl@65: public string iCurrentClientSessionId; sl@65: ClientData iCurrentClientData; sl@65: // sl@29: public bool iClosing; StephaneLenclud@122: // StephaneLenclud@122: public bool iSkipFrameRendering; sl@65: //Function pointer for pixel color filtering sl@58: ColorProcessingDelegate iColorFx; sl@65: //Function pointer for pixel X coordinate intercept sl@58: CoordinateTranslationDelegate iScreenX; sl@65: //Function pointer for pixel Y coordinate intercept sl@58: CoordinateTranslationDelegate iScreenY; StephaneLenclud@112: //NAudio StephaneLenclud@112: private MMDeviceEnumerator iMultiMediaDeviceEnumerator; StephaneLenclud@112: private MMDevice iMultiMediaDevice; StephaneLenclud@117: //Network StephaneLenclud@117: private NetworkManager iNetworkManager; StephaneLenclud@167: StephaneLenclud@167: /// StephaneLenclud@167: /// CEC - Consumer Electronic Control. StephaneLenclud@167: /// Notably used to turn TV on and off as Windows broadcast monitor on and off notifications. StephaneLenclud@167: /// StephaneLenclud@167: private ConsumerElectronicControl iCecManager; StephaneLenclud@112: sl@94: /// sl@94: /// Manage run when Windows startup option sl@94: /// sl@92: private StartupManager iStartupManager; sl@92: sl@94: /// StephaneLenclud@179: /// System notification icon used to hide our application from the task bar. sl@94: /// StephaneLenclud@173: private SharpLib.Notification.Control iNotifyIcon; sl@94: StephaneLenclud@167: /// StephaneLenclud@194: /// System recording notification icon. StephaneLenclud@179: /// StephaneLenclud@179: private SharpLib.Notification.Control iRecordingNotification; StephaneLenclud@179: Stephane@202: /// Stephane@202: /// Stephane@202: /// Stephane@202: RichTextBoxTextWriter iWriter; Stephane@202: StephaneLenclud@179: StephaneLenclud@179: /// StephaneLenclud@167: /// Allow user to receive window messages; StephaneLenclud@167: /// StephaneLenclud@167: public event WndProcDelegate OnWndProc; StephaneLenclud@167: sl@0: public MainForm() sl@0: { Stephane@212: ManagerEventAction.Current = Properties.Settings.Default.Actions; Stephane@212: if (ManagerEventAction.Current == null) Stephane@212: { Stephane@212: //No actions in our settings yet Stephane@212: ManagerEventAction.Current = new ManagerEventAction(); Stephane@212: Properties.Settings.Default.Actions = ManagerEventAction.Current; Stephane@212: } Stephane@212: else Stephane@212: { Stephane@212: //We loaded actions from our settings Stephane@212: //We need to hook them with corresponding events Stephane@212: ManagerEventAction.Current.Init(); Stephane@212: } StephaneLenclud@210: iSkipFrameRendering = false; StephaneLenclud@122: iClosing = false; sl@65: iCurrentClientSessionId = ""; sl@65: iCurrentClientData = null; sl@2: LastTickTime = DateTime.Now; StephaneLenclud@104: //Instantiate our display and register for events notifications sl@3: iDisplay = new Display(); StephaneLenclud@104: iDisplay.OnOpened += OnDisplayOpened; StephaneLenclud@104: iDisplay.OnClosed += OnDisplayClosed; StephaneLenclud@104: // StephaneLenclud@104: iClients = new Dictionary(); sl@92: iStartupManager = new StartupManager(); StephaneLenclud@173: iNotifyIcon = new SharpLib.Notification.Control(); StephaneLenclud@179: iRecordingNotification = new SharpLib.Notification.Control(); sl@2: StephaneLenclud@179: //Have our designer initialize its controls sl@0: InitializeComponent(); StephaneLenclud@104: StephaneLenclud@201: //Redirect console output Stephane@202: iWriter = new RichTextBoxTextWriter(richTextBoxLogs); Stephane@202: Console.SetOut(iWriter); StephaneLenclud@201: StephaneLenclud@201: //Populate device types StephaneLenclud@201: PopulateDeviceTypes(); StephaneLenclud@104: StephaneLenclud@152: //Populate optical drives StephaneLenclud@152: PopulateOpticalDrives(); StephaneLenclud@152: StephaneLenclud@104: //Initial status update sl@7: UpdateStatus(); StephaneLenclud@104: sl@14: //We have a bug when drawing minimized and reusing our bitmap StephaneLenclud@158: //Though I could not reproduce it on Windows 10 StephaneLenclud@175: iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height, PixelFormat.Format32bppArgb); sl@14: iCreateBitmap = false; sl@94: StephaneLenclud@104: //Minimize our window if desired sl@94: if (Properties.Settings.Default.StartMinimized) sl@94: { sl@94: WindowState = FormWindowState.Minimized; sl@94: } sl@94: sl@0: } sl@0: sl@94: /// StephaneLenclud@106: /// StephaneLenclud@106: /// StephaneLenclud@106: /// StephaneLenclud@106: /// StephaneLenclud@106: private void MainForm_Load(object sender, EventArgs e) StephaneLenclud@106: { StephaneLenclud@106: //Check if we are running a Click Once deployed application StephaneLenclud@106: if (ApplicationDeployment.IsNetworkDeployed) StephaneLenclud@106: { StephaneLenclud@106: //This is a proper Click Once installation, fetch and show our version number StephaneLenclud@106: this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion; StephaneLenclud@106: } StephaneLenclud@106: else StephaneLenclud@106: { StephaneLenclud@106: //Not a proper Click Once installation, assuming development build then StephaneLenclud@106: this.Text += " - development"; StephaneLenclud@106: } StephaneLenclud@106: StephaneLenclud@112: //NAudio StephaneLenclud@112: iMultiMediaDeviceEnumerator = new MMDeviceEnumerator(); StephaneLenclud@117: iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this); StephaneLenclud@112: UpdateAudioDeviceAndMasterVolumeThreadSafe(); StephaneLenclud@112: StephaneLenclud@117: //Network StephaneLenclud@117: iNetworkManager = new NetworkManager(); StephaneLenclud@117: iNetworkManager.OnConnectivityChanged += OnConnectivityChanged; StephaneLenclud@117: UpdateNetworkStatus(); StephaneLenclud@117: StephaneLenclud@167: //CEC StephaneLenclud@167: iCecManager = new ConsumerElectronicControl(); StephaneLenclud@167: OnWndProc += iCecManager.OnWndProc; StephaneLenclud@168: ResetCec(); StephaneLenclud@168: StephaneLenclud@211: //Setup Events StephaneLenclud@211: SetupEvents(); StephaneLenclud@167: StephaneLenclud@167: //Setup notification icon StephaneLenclud@167: SetupTrayIcon(); StephaneLenclud@106: StephaneLenclud@179: //Setup recording notification StephaneLenclud@179: SetupRecordingNotification(); StephaneLenclud@179: StephaneLenclud@179: // To make sure start up with minimize to tray works StephaneLenclud@179: if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray) StephaneLenclud@106: { StephaneLenclud@106: Visible = false; StephaneLenclud@106: } StephaneLenclud@106: StephaneLenclud@106: #if !DEBUG StephaneLenclud@106: //When not debugging we want the screen to be empty until a client takes over StephaneLenclud@106: ClearLayout(); StephaneLenclud@106: #else StephaneLenclud@106: //When developing we want at least one client for testing StephaneLenclud@106: StartNewClient("abcdefghijklmnopqrst-0123456789","ABCDEFGHIJKLMNOPQRST-0123456789"); StephaneLenclud@106: #endif StephaneLenclud@106: StephaneLenclud@106: //Open display connection on start-up if needed StephaneLenclud@106: if (Properties.Settings.Default.DisplayConnectOnStartup) StephaneLenclud@106: { StephaneLenclud@106: OpenDisplayConnection(); StephaneLenclud@106: } StephaneLenclud@106: StephaneLenclud@106: //Start our server so that we can get client requests StephaneLenclud@106: StartServer(); StephaneLenclud@124: StephaneLenclud@124: //Register for HID events StephaneLenclud@124: RegisterHidDevices(); StephaneLenclud@194: StephaneLenclud@194: //Start Idle client if needed StephaneLenclud@194: if (Properties.Settings.Default.StartIdleClient) StephaneLenclud@194: { StephaneLenclud@194: StartIdleClient(); StephaneLenclud@194: } StephaneLenclud@106: } StephaneLenclud@106: StephaneLenclud@106: /// StephaneLenclud@104: /// Called when our display is opened. StephaneLenclud@104: /// StephaneLenclud@104: /// StephaneLenclud@104: private void OnDisplayOpened(Display aDisplay) StephaneLenclud@104: { StephaneLenclud@187: //Make sure we resume frame rendering StephaneLenclud@187: iSkipFrameRendering = false; StephaneLenclud@122: StephaneLenclud@105: //Set our screen size now that our display is connected StephaneLenclud@105: //Our panelDisplay is the container of our tableLayoutPanel StephaneLenclud@105: //tableLayoutPanel will resize itself to fit the client size of our panelDisplay StephaneLenclud@105: //panelDisplay needs an extra 2 pixels for borders on each sides StephaneLenclud@105: //tableLayoutPanel will eventually be the exact size of our display StephaneLenclud@105: Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2); StephaneLenclud@105: panelDisplay.Size = size; StephaneLenclud@105: StephaneLenclud@104: //Our display was just opened, update our UI StephaneLenclud@104: UpdateStatus(); StephaneLenclud@104: //Initiate asynchronous request StephaneLenclud@104: iDisplay.RequestFirmwareRevision(); StephaneLenclud@108: StephaneLenclud@117: //Audio StephaneLenclud@112: UpdateMasterVolumeThreadSafe(); StephaneLenclud@117: //Network StephaneLenclud@117: UpdateNetworkStatus(); StephaneLenclud@112: StephaneLenclud@108: #if DEBUG StephaneLenclud@108: //Testing icon in debug, no arm done if icon not supported StephaneLenclud@109: //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1); StephaneLenclud@112: //iDisplay.SetAllIconsStatus(2); StephaneLenclud@108: #endif StephaneLenclud@108: StephaneLenclud@104: } StephaneLenclud@104: StephaneLenclud@211: /// StephaneLenclud@211: /// StephaneLenclud@211: /// StephaneLenclud@211: private void SetupEvents() StephaneLenclud@211: { StephaneLenclud@211: //Reset our tree StephaneLenclud@211: iTreeViewEvents.Nodes.Clear(); StephaneLenclud@211: //Populate registered events Stephane@212: foreach (string key in ManagerEventAction.Current.Events.Keys) StephaneLenclud@211: { Stephane@212: Event e = ManagerEventAction.Current.Events[key]; StephaneLenclud@211: TreeNode eventNode = iTreeViewEvents.Nodes.Add(key,e.Name); StephaneLenclud@211: eventNode.Tag = e; StephaneLenclud@211: eventNode.Nodes.Add(key + ".Description", e.Description); StephaneLenclud@211: TreeNode actionsNodes = eventNode.Nodes.Add(key + ".Actions", "Actions"); StephaneLenclud@211: StephaneLenclud@211: foreach (SharpLib.Ear.Action a in e.Actions) StephaneLenclud@211: { StephaneLenclud@211: actionsNodes.Nodes.Add(a.Name); StephaneLenclud@211: } StephaneLenclud@211: } StephaneLenclud@211: StephaneLenclud@211: } StephaneLenclud@211: StephaneLenclud@104: /// StephaneLenclud@104: /// Called when our display is closed. StephaneLenclud@104: /// StephaneLenclud@104: /// StephaneLenclud@104: private void OnDisplayClosed(Display aDisplay) StephaneLenclud@104: { StephaneLenclud@187: //Our display was just closed, update our UI consequently StephaneLenclud@187: UpdateStatus(); StephaneLenclud@104: } StephaneLenclud@117: StephaneLenclud@117: public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity) StephaneLenclud@117: { StephaneLenclud@117: //Update network status StephaneLenclud@117: UpdateNetworkStatus(); StephaneLenclud@117: } StephaneLenclud@117: StephaneLenclud@117: /// StephaneLenclud@117: /// Update our Network Status StephaneLenclud@117: /// StephaneLenclud@117: private void UpdateNetworkStatus() StephaneLenclud@117: { StephaneLenclud@117: if (iDisplay.IsOpen()) StephaneLenclud@117: { StephaneLenclud@135: iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet, iNetworkManager.NetworkListManager.IsConnectedToInternet); StephaneLenclud@135: iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected); StephaneLenclud@117: } StephaneLenclud@117: } StephaneLenclud@117: StephaneLenclud@117: StephaneLenclud@118: int iLastNetworkIconIndex = 0; StephaneLenclud@118: int iUpdateCountSinceLastNetworkAnimation = 0; StephaneLenclud@118: StephaneLenclud@118: /// StephaneLenclud@118: /// StephaneLenclud@118: /// StephaneLenclud@118: private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime) StephaneLenclud@118: { StephaneLenclud@118: iUpdateCountSinceLastNetworkAnimation++; StephaneLenclud@118: iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation % 4; StephaneLenclud@118: StephaneLenclud@118: if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && iUpdateCountSinceLastNetworkAnimation==0) StephaneLenclud@135: { StephaneLenclud@135: int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal); StephaneLenclud@120: if (iconCount <= 0) StephaneLenclud@120: { StephaneLenclud@120: //Prevents div by zero and other undefined behavior StephaneLenclud@120: return; StephaneLenclud@120: } StephaneLenclud@118: iLastNetworkIconIndex++; StephaneLenclud@119: iLastNetworkIconIndex = iLastNetworkIconIndex % (iconCount*2); StephaneLenclud@118: for (int i=0;i 3) && !(i == 1 && iLastNetworkIconIndex > 4)) StephaneLenclud@118: { StephaneLenclud@135: iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i); StephaneLenclud@118: } StephaneLenclud@118: else StephaneLenclud@118: { StephaneLenclud@135: iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i); StephaneLenclud@118: } StephaneLenclud@118: } StephaneLenclud@118: } StephaneLenclud@118: } StephaneLenclud@118: StephaneLenclud@118: StephaneLenclud@118: StephaneLenclud@112: /// StephaneLenclud@112: /// Receive volume change notification and reflect changes on our slider. StephaneLenclud@112: /// StephaneLenclud@112: /// StephaneLenclud@112: public void OnVolumeNotificationThreadSafe(AudioVolumeNotificationData data) StephaneLenclud@112: { StephaneLenclud@112: UpdateMasterVolumeThreadSafe(); StephaneLenclud@112: } StephaneLenclud@112: StephaneLenclud@112: /// StephaneLenclud@112: /// Update master volume when user moves our slider. StephaneLenclud@112: /// StephaneLenclud@112: /// StephaneLenclud@112: /// StephaneLenclud@112: private void trackBarMasterVolume_Scroll(object sender, EventArgs e) StephaneLenclud@112: { StephaneLenclud@116: //Just like Windows Volume Mixer we unmute if the volume is adjusted StephaneLenclud@116: iMultiMediaDevice.AudioEndpointVolume.Mute = false; StephaneLenclud@116: //Set volume level according to our volume slider new position StephaneLenclud@112: iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value / 100.0f; StephaneLenclud@112: } StephaneLenclud@112: StephaneLenclud@113: StephaneLenclud@113: /// StephaneLenclud@113: /// Mute check box changed. StephaneLenclud@113: /// StephaneLenclud@113: /// StephaneLenclud@113: /// StephaneLenclud@113: private void checkBoxMute_CheckedChanged(object sender, EventArgs e) StephaneLenclud@113: { StephaneLenclud@113: iMultiMediaDevice.AudioEndpointVolume.Mute = checkBoxMute.Checked; StephaneLenclud@113: } StephaneLenclud@113: StephaneLenclud@112: /// StephaneLenclud@112: /// Device State Changed StephaneLenclud@112: /// StephaneLenclud@112: public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, [MarshalAs(UnmanagedType.I4)] DeviceState newState){} StephaneLenclud@112: StephaneLenclud@112: /// StephaneLenclud@112: /// Device Added StephaneLenclud@112: /// StephaneLenclud@112: public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) { } StephaneLenclud@112: StephaneLenclud@112: /// StephaneLenclud@112: /// Device Removed StephaneLenclud@112: /// StephaneLenclud@112: public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) { } StephaneLenclud@112: StephaneLenclud@112: /// StephaneLenclud@112: /// Default Device Changed StephaneLenclud@112: /// StephaneLenclud@112: public void OnDefaultDeviceChanged(DataFlow flow, Role role, [MarshalAs(UnmanagedType.LPWStr)] string defaultDeviceId) StephaneLenclud@112: { StephaneLenclud@112: if (role == Role.Multimedia && flow == DataFlow.Render) StephaneLenclud@112: { StephaneLenclud@112: UpdateAudioDeviceAndMasterVolumeThreadSafe(); StephaneLenclud@112: } StephaneLenclud@112: } StephaneLenclud@112: StephaneLenclud@112: /// StephaneLenclud@112: /// Property Value Changed StephaneLenclud@112: /// StephaneLenclud@112: /// StephaneLenclud@112: /// StephaneLenclud@112: public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key){} StephaneLenclud@112: StephaneLenclud@112: StephaneLenclud@112: StephaneLenclud@112: StephaneLenclud@112: /// StephaneLenclud@116: /// Update master volume indicators based our current system states. StephaneLenclud@116: /// This typically includes volume levels and mute status. StephaneLenclud@112: /// StephaneLenclud@112: private void UpdateMasterVolumeThreadSafe() StephaneLenclud@112: { StephaneLenclud@112: if (this.InvokeRequired) StephaneLenclud@112: { StephaneLenclud@112: //Not in the proper thread, invoke ourselves StephaneLenclud@112: PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe); StephaneLenclud@112: this.Invoke(d, new object[] { }); StephaneLenclud@112: return; StephaneLenclud@112: } StephaneLenclud@112: StephaneLenclud@113: //Update volume slider StephaneLenclud@112: float volumeLevelScalar = iMultiMediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar; StephaneLenclud@112: trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar * 100); StephaneLenclud@113: //Update mute checkbox StephaneLenclud@113: checkBoxMute.Checked = iMultiMediaDevice.AudioEndpointVolume.Mute; StephaneLenclud@112: StephaneLenclud@116: //If our display connection is open we need to update its icons StephaneLenclud@112: if (iDisplay.IsOpen()) StephaneLenclud@112: { StephaneLenclud@116: //First take care our our volume level icons StephaneLenclud@135: int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume); StephaneLenclud@112: if (volumeIconCount > 0) StephaneLenclud@114: { StephaneLenclud@116: //Compute current volume level from system level and the number of segments in our display volume bar. StephaneLenclud@116: //That tells us how many segments in our volume bar needs to be turned on. StephaneLenclud@116: float currentVolume = volumeLevelScalar * volumeIconCount; StephaneLenclud@116: int segmentOnCount = Convert.ToInt32(currentVolume); StephaneLenclud@116: //Check if our segment count was rounded up, this will later be used for half brightness segment StephaneLenclud@116: bool roundedUp = segmentOnCount > currentVolume; StephaneLenclud@114: StephaneLenclud@112: for (int i = 0; i < volumeIconCount; i++) StephaneLenclud@112: { StephaneLenclud@116: if (i < segmentOnCount) StephaneLenclud@112: { StephaneLenclud@116: //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness. StephaneLenclud@116: if (i == segmentOnCount - 1 && roundedUp) StephaneLenclud@114: { StephaneLenclud@114: //Half brightness StephaneLenclud@135: iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1) / 2); StephaneLenclud@114: } StephaneLenclud@114: else StephaneLenclud@114: { StephaneLenclud@114: //Full brightness StephaneLenclud@135: iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1); StephaneLenclud@114: } StephaneLenclud@112: } StephaneLenclud@112: else StephaneLenclud@112: { StephaneLenclud@135: iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0); StephaneLenclud@112: } StephaneLenclud@112: } StephaneLenclud@112: } StephaneLenclud@113: Stephane@182: //Take care of our mute icon StephaneLenclud@135: iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iMultiMediaDevice.AudioEndpointVolume.Mute); StephaneLenclud@112: } StephaneLenclud@112: StephaneLenclud@112: } StephaneLenclud@112: StephaneLenclud@112: /// StephaneLenclud@112: /// StephaneLenclud@112: /// StephaneLenclud@112: private void UpdateAudioDeviceAndMasterVolumeThreadSafe() StephaneLenclud@112: { StephaneLenclud@112: if (this.InvokeRequired) StephaneLenclud@112: { StephaneLenclud@112: //Not in the proper thread, invoke ourselves StephaneLenclud@112: PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe); StephaneLenclud@112: this.Invoke(d, new object[] { }); StephaneLenclud@112: return; StephaneLenclud@112: } StephaneLenclud@112: StephaneLenclud@112: //We are in the correct thread just go ahead. StephaneLenclud@112: try StephaneLenclud@112: { StephaneLenclud@112: //Get our master volume StephaneLenclud@112: iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); StephaneLenclud@116: //Update our label StephaneLenclud@116: labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName; StephaneLenclud@116: StephaneLenclud@112: //Show our volume in our track bar StephaneLenclud@112: UpdateMasterVolumeThreadSafe(); StephaneLenclud@112: StephaneLenclud@112: //Register to get volume modifications StephaneLenclud@112: iMultiMediaDevice.AudioEndpointVolume.OnVolumeNotification += OnVolumeNotificationThreadSafe; StephaneLenclud@112: // StephaneLenclud@112: trackBarMasterVolume.Enabled = true; StephaneLenclud@112: } StephaneLenclud@112: catch (Exception ex) StephaneLenclud@112: { StephaneLenclud@112: Debug.WriteLine("Exception thrown in UpdateAudioDeviceAndMasterVolume"); StephaneLenclud@112: Debug.WriteLine(ex.ToString()); StephaneLenclud@112: //Something went wrong S/PDIF device ca throw exception I guess StephaneLenclud@112: trackBarMasterVolume.Enabled = false; StephaneLenclud@112: } StephaneLenclud@112: } StephaneLenclud@104: StephaneLenclud@104: /// StephaneLenclud@104: /// StephaneLenclud@104: /// StephaneLenclud@104: private void PopulateDeviceTypes() StephaneLenclud@104: { StephaneLenclud@104: int count = Display.TypeCount(); StephaneLenclud@104: StephaneLenclud@106: for (int i = 0; i < count; i++) StephaneLenclud@104: { StephaneLenclud@135: comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type)i)); StephaneLenclud@106: } StephaneLenclud@104: } StephaneLenclud@104: StephaneLenclud@152: /// StephaneLenclud@152: /// StephaneLenclud@152: /// StephaneLenclud@152: private void PopulateOpticalDrives() StephaneLenclud@152: { StephaneLenclud@152: //Reset our list of drives StephaneLenclud@152: comboBoxOpticalDrives.Items.Clear(); StephaneLenclud@153: comboBoxOpticalDrives.Items.Add("None"); StephaneLenclud@152: StephaneLenclud@152: //Go through each drives on our system and collected the optical ones in our list StephaneLenclud@152: DriveInfo[] allDrives = DriveInfo.GetDrives(); StephaneLenclud@152: foreach (DriveInfo d in allDrives) StephaneLenclud@152: { StephaneLenclud@157: Debug.WriteLine("Drive " + d.Name); StephaneLenclud@152: Debug.WriteLine(" Drive type: {0}", d.DriveType); StephaneLenclud@152: StephaneLenclud@152: if (d.DriveType==DriveType.CDRom) StephaneLenclud@152: { StephaneLenclud@152: //This is an optical drive, add it now StephaneLenclud@152: comboBoxOpticalDrives.Items.Add(d.Name.Substring(0,2)); StephaneLenclud@152: } StephaneLenclud@153: } StephaneLenclud@152: } StephaneLenclud@152: StephaneLenclud@152: /// StephaneLenclud@152: /// StephaneLenclud@152: /// StephaneLenclud@152: /// StephaneLenclud@152: public string OpticalDriveToEject() StephaneLenclud@152: { StephaneLenclud@153: return comboBoxOpticalDrives.SelectedItem.ToString(); StephaneLenclud@152: } StephaneLenclud@152: StephaneLenclud@152: StephaneLenclud@152: StephaneLenclud@104: /// sl@99: /// sl@94: /// sl@95: private void SetupTrayIcon() sl@95: { sl@95: iNotifyIcon.Icon = GetIcon("vfd.ico"); sl@95: iNotifyIcon.Text = "Sharp Display Manager"; sl@95: iNotifyIcon.Visible = true; sl@95: sl@95: //Double click toggles visibility - typically brings up the application sl@95: iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args) sl@95: { sl@95: SysTrayHideShow(); sl@95: }; sl@95: sl@95: //Adding a context menu, useful to be able to exit the application sl@95: ContextMenu contextMenu = new ContextMenu(); sl@95: //Context menu item to toggle visibility sl@95: MenuItem hideShowItem = new MenuItem("Hide/Show"); sl@95: hideShowItem.Click += delegate(object obj, EventArgs args) sl@95: { sl@95: SysTrayHideShow(); sl@95: }; sl@95: contextMenu.MenuItems.Add(hideShowItem); sl@95: sl@95: //Context menu item separator sl@95: contextMenu.MenuItems.Add(new MenuItem("-")); sl@95: sl@95: //Context menu exit item sl@95: MenuItem exitItem = new MenuItem("Exit"); sl@95: exitItem.Click += delegate(object obj, EventArgs args) sl@95: { sl@95: Application.Exit(); sl@95: }; sl@95: contextMenu.MenuItems.Add(exitItem); sl@95: sl@95: iNotifyIcon.ContextMenu = contextMenu; sl@95: } sl@95: StephaneLenclud@178: /// StephaneLenclud@179: /// StephaneLenclud@179: /// StephaneLenclud@179: private void SetupRecordingNotification() StephaneLenclud@179: { StephaneLenclud@179: iRecordingNotification.Icon = GetIcon("record.ico"); StephaneLenclud@179: iRecordingNotification.Text = "No recording"; StephaneLenclud@180: iRecordingNotification.Visible = false; StephaneLenclud@179: } StephaneLenclud@179: StephaneLenclud@179: /// StephaneLenclud@178: /// Access icons from embedded resources. StephaneLenclud@178: /// StephaneLenclud@178: /// StephaneLenclud@178: /// StephaneLenclud@178: public static Icon GetIcon(string aName) sl@94: { StephaneLenclud@178: string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames(); StephaneLenclud@178: foreach (string name in names) sl@94: { StephaneLenclud@178: //Find a resource name that ends with the given name StephaneLenclud@178: if (name.EndsWith(aName)) sl@94: { StephaneLenclud@178: using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name)) sl@94: { sl@94: return new Icon(stream); sl@94: } sl@94: } sl@94: } sl@94: sl@94: return null; sl@94: } sl@94: sl@94: sl@65: /// sl@65: /// Set our current client. sl@65: /// This will take care of applying our client layout and set data fields. sl@65: /// sl@65: /// StephaneLenclud@141: void SetCurrentClient(string aSessionId, bool aForce=false) sl@57: { sl@65: if (aSessionId == iCurrentClientSessionId) sl@57: { sl@65: //Given client is already the current one. sl@65: //Don't bother changing anything then. sl@65: return; sl@65: } sl@57: StephaneLenclud@185: ClientData requestedClientData = iClients[aSessionId]; StephaneLenclud@141: StephaneLenclud@141: //Check when was the last time we switched to that client StephaneLenclud@142: if (iCurrentClientData != null) StephaneLenclud@141: { StephaneLenclud@185: //Do not switch client if priority of current client is higher StephaneLenclud@185: if (!aForce && requestedClientData.Priority < iCurrentClientData.Priority) StephaneLenclud@185: { StephaneLenclud@185: return; StephaneLenclud@185: } StephaneLenclud@185: StephaneLenclud@185: StephaneLenclud@142: double lastSwitchToClientSecondsAgo = (DateTime.Now - iCurrentClientData.LastSwitchTime).TotalSeconds; StephaneLenclud@142: //TODO: put that hard coded value as a client property StephaneLenclud@142: //Clients should be able to define how often they can be interrupted StephaneLenclud@142: //Thus a background client can set this to zero allowing any other client to interrupt at any time StephaneLenclud@142: //We could also compute this delay by looking at the requests frequencies? StephaneLenclud@185: if (!aForce && StephaneLenclud@185: requestedClientData.Priority == iCurrentClientData.Priority && //Time sharing is only if clients have the same priority StephaneLenclud@185: (lastSwitchToClientSecondsAgo < 30)) //Make sure a client is on for at least 30 seconds StephaneLenclud@142: { StephaneLenclud@142: //Don't switch clients too often StephaneLenclud@142: return; StephaneLenclud@142: } StephaneLenclud@141: } StephaneLenclud@141: sl@65: //Set current client ID. sl@65: iCurrentClientSessionId = aSessionId; StephaneLenclud@141: //Set the time we last switched to that client StephaneLenclud@141: iClients[aSessionId].LastSwitchTime = DateTime.Now; sl@65: //Fetch and set current client data. StephaneLenclud@185: iCurrentClientData = requestedClientData; sl@65: //Apply layout and set data fields. sl@65: UpdateTableLayoutPanel(iCurrentClientData); sl@57: } sl@57: sl@0: private void buttonFont_Click(object sender, EventArgs e) sl@0: { sl@0: //fontDialog.ShowColor = true; sl@0: //fontDialog.ShowApply = true; sl@0: fontDialog.ShowEffects = true; sl@99: fontDialog.Font = cds.Font; sl@28: sl@28: fontDialog.FixedPitchOnly = checkBoxFixedPitchFontOnly.Checked; sl@28: sl@0: //fontDialog.ShowHelp = true; sl@0: sl@0: //fontDlg.MaxSize = 40; sl@0: //fontDlg.MinSize = 22; sl@0: sl@0: //fontDialog.Parent = this; sl@0: //fontDialog.StartPosition = FormStartPosition.CenterParent; sl@0: sl@0: //DlgBox.ShowDialog(fontDialog); sl@0: sl@0: //if (fontDialog.ShowDialog(this) != DialogResult.Cancel) sl@0: if (DlgBox.ShowDialog(fontDialog) != DialogResult.Cancel) sl@0: { sl@99: //Set the fonts to all our labels in our layout StephaneLenclud@175: foreach (Control ctrl in iTableLayoutPanel.Controls) sl@99: { sl@99: if (ctrl is MarqueeLabel) sl@99: { sl@99: ((MarqueeLabel)ctrl).Font = fontDialog.Font; sl@99: } sl@99: } sl@0: sl@99: //Save font settings sl@48: cds.Font = fontDialog.Font; sl@8: Properties.Settings.Default.Save(); sl@36: // sl@37: CheckFontHeight(); sl@37: } sl@37: } sl@36: sl@37: /// sl@38: /// sl@37: /// sl@37: void CheckFontHeight() sl@37: { sl@54: //Show font height and width sl@54: labelFontHeight.Text = "Font height: " + cds.Font.Height; sl@54: float charWidth = IsFixedWidth(cds.Font); sl@54: if (charWidth == 0.0f) sl@54: { sl@54: labelFontWidth.Visible = false; sl@54: } sl@54: else sl@54: { sl@54: labelFontWidth.Visible = true; sl@54: labelFontWidth.Text = "Font width: " + charWidth; sl@54: } sl@54: sl@70: MarqueeLabel label = null; sl@68: //Get the first label control we can find StephaneLenclud@175: foreach (Control ctrl in iTableLayoutPanel.Controls) sl@68: { sl@68: if (ctrl is MarqueeLabel) sl@68: { sl@68: label = (MarqueeLabel)ctrl; sl@68: break; sl@68: } sl@68: } sl@68: sl@54: //Now check font height and show a warning if needed. sl@68: if (label != null && label.Font.Height > label.Height) sl@37: { sl@60: labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + " pixels!"; sl@37: labelWarning.Visible = true; sl@0: } sl@37: else sl@37: { sl@37: labelWarning.Visible = false; sl@37: } sl@37: sl@0: } sl@0: sl@0: private void buttonCapture_Click(object sender, EventArgs e) sl@0: { StephaneLenclud@175: System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height); StephaneLenclud@175: iTableLayoutPanel.DrawToBitmap(bmp, iTableLayoutPanel.ClientRectangle); sl@14: //Bitmap bmpToSave = new Bitmap(bmp); sl@14: bmp.Save("D:\\capture.png"); sl@14: StephaneLenclud@175: ((MarqueeLabel)iTableLayoutPanel.Controls[0]).Text = "Captured"; sl@17: sl@14: /* sl@14: string outputFileName = "d:\\capture.png"; sl@14: using (MemoryStream memory = new MemoryStream()) sl@14: { sl@14: using (FileStream fs = new FileStream(outputFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite)) sl@14: { sl@14: bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Png); sl@14: byte[] bytes = memory.ToArray(); sl@14: fs.Write(bytes, 0, bytes.Length); sl@14: } sl@14: } sl@14: */ sl@14: sl@0: } sl@2: sl@12: private void CheckForRequestResults() sl@12: { sl@12: if (iDisplay.IsRequestPending()) sl@12: { sl@12: switch (iDisplay.AttemptRequestCompletion()) sl@12: { StephaneLenclud@135: case MiniDisplay.Request.FirmwareRevision: sl@51: toolStripStatusLabelConnect.Text += " v" + iDisplay.FirmwareRevision(); sl@51: //Issue next request then sl@51: iDisplay.RequestPowerSupplyStatus(); sl@51: break; sl@51: StephaneLenclud@135: case MiniDisplay.Request.PowerSupplyStatus: sl@12: if (iDisplay.PowerSupplyStatus()) sl@12: { sl@12: toolStripStatusLabelPower.Text = "ON"; sl@12: } sl@12: else sl@12: { sl@12: toolStripStatusLabelPower.Text = "OFF"; sl@12: } sl@12: //Issue next request then sl@12: iDisplay.RequestDeviceId(); sl@12: break; sl@12: StephaneLenclud@135: case MiniDisplay.Request.DeviceId: sl@12: toolStripStatusLabelConnect.Text += " - " + iDisplay.DeviceId(); sl@12: //No more request to issue sl@12: break; sl@12: } sl@12: } sl@12: } sl@12: sl@58: public static uint ColorWhiteIsOn(int aX, int aY, uint aPixel) sl@58: { sl@58: if ((aPixel & 0x00FFFFFF) == 0x00FFFFFF) sl@58: { sl@58: return 0xFFFFFFFF; sl@58: } sl@58: return 0x00000000; sl@58: } sl@16: sl@58: public static uint ColorUntouched(int aX, int aY, uint aPixel) sl@57: { sl@57: return aPixel; sl@57: } sl@57: sl@58: public static uint ColorInversed(int aX, int aY, uint aPixel) sl@57: { sl@57: return ~aPixel; sl@57: } sl@57: sl@58: public static uint ColorChessboard(int aX, int aY, uint aPixel) sl@58: { sl@58: if ((aX % 2 == 0) && (aY % 2 == 0)) sl@58: { sl@58: return ~aPixel; sl@58: } sl@58: else if ((aX % 2 != 0) && (aY % 2 != 0)) sl@58: { sl@58: return ~aPixel; sl@58: } sl@58: return 0x00000000; sl@58: } sl@58: sl@16: sl@16: public static int ScreenReversedX(System.Drawing.Bitmap aBmp, int aX) sl@16: { sl@16: return aBmp.Width - aX - 1; sl@16: } sl@16: sl@16: public int ScreenReversedY(System.Drawing.Bitmap aBmp, int aY) sl@16: { sl@16: return iBmp.Height - aY - 1; sl@16: } sl@16: sl@16: public int ScreenX(System.Drawing.Bitmap aBmp, int aX) sl@16: { sl@16: return aX; sl@16: } sl@16: sl@16: public int ScreenY(System.Drawing.Bitmap aBmp, int aY) sl@16: { sl@16: return aY; sl@16: } sl@16: sl@58: /// sl@58: /// Select proper pixel delegates according to our current settings. sl@58: /// sl@58: private void SetupPixelDelegates() sl@58: { sl@59: //Select our pixel processing routine sl@58: if (cds.InverseColors) sl@58: { sl@58: //iColorFx = ColorChessboard; sl@58: iColorFx = ColorInversed; sl@58: } sl@58: else sl@58: { sl@58: iColorFx = ColorWhiteIsOn; sl@58: } sl@58: sl@58: //Select proper coordinate translation functions sl@58: //We used delegate/function pointer to support reverse screen without doing an extra test on each pixels sl@58: if (cds.ReverseScreen) sl@58: { sl@58: iScreenX = ScreenReversedX; sl@58: iScreenY = ScreenReversedY; sl@58: } sl@58: else sl@58: { sl@58: iScreenX = ScreenX; sl@58: iScreenY = ScreenY; sl@58: } sl@58: sl@58: } sl@16: sl@16: //This is our timer tick responsible to perform our render sl@2: private void timer_Tick(object sender, EventArgs e) sl@14: { StephaneLenclud@206: //Not ideal cause this has nothing to do with display render StephaneLenclud@206: LogsUpdate(); StephaneLenclud@206: sl@2: //Update our animations sl@2: DateTime NewTickTime = DateTime.Now; sl@2: StephaneLenclud@118: UpdateNetworkSignal(LastTickTime, NewTickTime); StephaneLenclud@118: sl@60: //Update animation for all our marquees StephaneLenclud@175: foreach (Control ctrl in iTableLayoutPanel.Controls) sl@60: { sl@68: if (ctrl is MarqueeLabel) sl@68: { sl@68: ((MarqueeLabel)ctrl).UpdateAnimation(LastTickTime, NewTickTime); sl@68: } sl@60: } sl@60: sl@4: //Update our display sl@4: if (iDisplay.IsOpen()) sl@4: { sl@12: CheckForRequestResults(); sl@12: StephaneLenclud@122: //Check if frame rendering is needed StephaneLenclud@122: //Typically used when showing clock StephaneLenclud@122: if (!iSkipFrameRendering) StephaneLenclud@122: { StephaneLenclud@122: //Draw to bitmap StephaneLenclud@122: if (iCreateBitmap) StephaneLenclud@122: { StephaneLenclud@175: iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height, PixelFormat.Format32bppArgb); StephaneLenclud@158: iCreateBitmap = false; StephaneLenclud@158: } StephaneLenclud@175: iTableLayoutPanel.DrawToBitmap(iBmp, iTableLayoutPanel.ClientRectangle); StephaneLenclud@122: //iBmp.Save("D:\\capture.png"); sl@16: StephaneLenclud@122: //Send it to our display StephaneLenclud@122: for (int i = 0; i < iBmp.Width; i++) StephaneLenclud@122: { StephaneLenclud@122: for (int j = 0; j < iBmp.Height; j++) StephaneLenclud@122: { StephaneLenclud@122: unchecked StephaneLenclud@122: { StephaneLenclud@122: //Get our processed pixel coordinates StephaneLenclud@122: int x = iScreenX(iBmp, i); StephaneLenclud@122: int y = iScreenY(iBmp, j); StephaneLenclud@122: //Get pixel color StephaneLenclud@122: uint color = (uint)iBmp.GetPixel(i, j).ToArgb(); StephaneLenclud@122: //Apply color effects StephaneLenclud@122: color = iColorFx(x, y, color); StephaneLenclud@122: //Now set our pixel StephaneLenclud@122: iDisplay.SetPixel(x, y, color); StephaneLenclud@122: } StephaneLenclud@122: } StephaneLenclud@122: } sl@4: StephaneLenclud@122: iDisplay.SwapBuffers(); StephaneLenclud@122: } sl@4: } sl@8: sl@8: //Compute instant FPS sl@47: toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + (1000/timer.Interval).ToString() + " FPS"; sl@8: sl@8: LastTickTime = NewTickTime; sl@8: sl@2: } sl@3: StephaneLenclud@103: /// StephaneLenclud@103: /// Attempt to establish connection with our display hardware. StephaneLenclud@103: /// sl@46: private void OpenDisplayConnection() sl@3: { sl@46: CloseDisplayConnection(); sl@46: StephaneLenclud@135: if (!iDisplay.Open((MiniDisplay.Type)cds.DisplayType)) StephaneLenclud@104: { StephaneLenclud@104: UpdateStatus(); StephaneLenclud@104: toolStripStatusLabelConnect.Text = "Connection error"; sl@7: } sl@46: } sl@7: sl@46: private void CloseDisplayConnection() sl@46: { StephaneLenclud@104: //Status will be updated upon receiving the closed event StephaneLenclud@122: StephaneLenclud@122: if (iDisplay == null || !iDisplay.IsOpen()) StephaneLenclud@122: { StephaneLenclud@122: return; StephaneLenclud@122: } StephaneLenclud@122: StephaneLenclud@122: //Do not clear if we gave up on rendering already. StephaneLenclud@122: //This means we will keep on displaying clock on MDM166AA for instance. StephaneLenclud@122: if (!iSkipFrameRendering) StephaneLenclud@122: { StephaneLenclud@122: iDisplay.Clear(); StephaneLenclud@122: iDisplay.SwapBuffers(); StephaneLenclud@122: } StephaneLenclud@122: StephaneLenclud@122: iDisplay.SetAllIconsStatus(0); //Turn off all icons sl@46: iDisplay.Close(); sl@46: } sl@46: sl@46: private void buttonOpen_Click(object sender, EventArgs e) sl@46: { sl@46: OpenDisplayConnection(); sl@3: } sl@3: sl@3: private void buttonClose_Click(object sender, EventArgs e) sl@3: { sl@46: CloseDisplayConnection(); sl@3: } sl@3: sl@3: private void buttonClear_Click(object sender, EventArgs e) sl@3: { sl@3: iDisplay.Clear(); sl@3: iDisplay.SwapBuffers(); sl@3: } sl@3: sl@3: private void buttonFill_Click(object sender, EventArgs e) sl@3: { sl@3: iDisplay.Fill(); sl@3: iDisplay.SwapBuffers(); sl@3: } sl@3: sl@3: private void trackBarBrightness_Scroll(object sender, EventArgs e) sl@3: { sl@48: cds.Brightness = trackBarBrightness.Value; sl@9: Properties.Settings.Default.Save(); sl@3: iDisplay.SetBrightness(trackBarBrightness.Value); sl@9: sl@3: } sl@7: sl@48: sl@48: /// sl@48: /// CDS stands for Current Display Settings sl@48: /// sl@50: private DisplaySettings cds sl@48: { sl@48: get sl@48: { sl@65: DisplaysSettings settings = Properties.Settings.Default.DisplaysSettings; sl@51: if (settings == null) sl@51: { sl@51: settings = new DisplaysSettings(); sl@51: settings.Init(); sl@65: Properties.Settings.Default.DisplaysSettings = settings; sl@51: } sl@48: sl@48: //Make sure all our settings have been created sl@48: while (settings.Displays.Count <= Properties.Settings.Default.CurrentDisplayIndex) sl@48: { sl@50: settings.Displays.Add(new DisplaySettings()); sl@48: } sl@48: sl@50: DisplaySettings displaySettings = settings.Displays[Properties.Settings.Default.CurrentDisplayIndex]; sl@48: return displaySettings; sl@48: } sl@48: } sl@48: sl@54: /// sl@54: /// Check if the given font has a fixed character pitch. sl@54: /// sl@54: /// sl@54: /// 0.0f if this is not a monospace font, otherwise returns the character width. sl@54: public float IsFixedWidth(Font ft) sl@54: { sl@54: Graphics g = CreateGraphics(); sl@54: char[] charSizes = new char[] { 'i', 'a', 'Z', '%', '#', 'a', 'B', 'l', 'm', ',', '.' }; sl@54: float charWidth = g.MeasureString("I", ft, Int32.MaxValue, StringFormat.GenericTypographic).Width; sl@54: sl@54: bool fixedWidth = true; sl@54: sl@54: foreach (char c in charSizes) sl@54: if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != charWidth) sl@54: fixedWidth = false; sl@54: sl@54: if (fixedWidth) sl@54: { sl@54: return charWidth; sl@54: } sl@54: sl@54: return 0.0f; sl@54: } sl@54: StephaneLenclud@103: /// StephaneLenclud@103: /// Synchronize UI with settings StephaneLenclud@103: /// sl@7: private void UpdateStatus() StephaneLenclud@103: { sl@48: //Load settings sl@54: checkBoxShowBorders.Checked = cds.ShowBorders; StephaneLenclud@175: iTableLayoutPanel.CellBorderStyle = (cds.ShowBorders ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None); sl@60: sl@60: //Set the proper font to each of our labels StephaneLenclud@175: foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls) sl@60: { sl@60: ctrl.Font = cds.Font; sl@60: } sl@60: sl@54: CheckFontHeight(); sl@96: //Check if "run on Windows startup" is enabled sl@96: checkBoxAutoStart.Checked = iStartupManager.Startup; sl@96: // sl@48: checkBoxConnectOnStartup.Checked = Properties.Settings.Default.DisplayConnectOnStartup; sl@94: checkBoxMinimizeToTray.Checked = Properties.Settings.Default.MinimizeToTray; sl@94: checkBoxStartMinimized.Checked = Properties.Settings.Default.StartMinimized; StephaneLenclud@194: iCheckBoxStartIdleClient.Checked = Properties.Settings.Default.StartIdleClient; StephaneLenclud@194: labelStartFileName.Text = Properties.Settings.Default.StartFileName; StephaneLenclud@194: StephaneLenclud@153: StephaneLenclud@153: //Try find our drive in our drive list StephaneLenclud@153: int opticalDriveItemIndex=0; StephaneLenclud@153: bool driveNotFound = true; StephaneLenclud@153: string opticalDriveToEject=Properties.Settings.Default.OpticalDriveToEject; StephaneLenclud@153: foreach (object item in comboBoxOpticalDrives.Items) StephaneLenclud@153: { StephaneLenclud@154: if (opticalDriveToEject == item.ToString()) StephaneLenclud@153: { StephaneLenclud@153: comboBoxOpticalDrives.SelectedIndex = opticalDriveItemIndex; StephaneLenclud@153: driveNotFound = false; StephaneLenclud@153: break; StephaneLenclud@153: } StephaneLenclud@153: opticalDriveItemIndex++; StephaneLenclud@153: } StephaneLenclud@153: StephaneLenclud@153: if (driveNotFound) StephaneLenclud@153: { StephaneLenclud@153: //We could not find the drive we had saved. StephaneLenclud@153: //Select "None" then. StephaneLenclud@153: comboBoxOpticalDrives.SelectedIndex = 0; StephaneLenclud@153: } StephaneLenclud@153: StephaneLenclud@168: //CEC settings StephaneLenclud@168: checkBoxCecEnabled.Checked = Properties.Settings.Default.CecEnabled; StephaneLenclud@168: checkBoxCecMonitorOn.Checked = Properties.Settings.Default.CecMonitorOn; StephaneLenclud@168: checkBoxCecMonitorOff.Checked = Properties.Settings.Default.CecMonitorOff; StephaneLenclud@204: checkBoxCecReconnectToPowerTv.Checked = Properties.Settings.Default.CecReconnectToPowerTv; StephaneLenclud@168: comboBoxHdmiPort.SelectedIndex = Properties.Settings.Default.CecHdmiPort - 1; StephaneLenclud@153: StephaneLenclud@168: //Mini Display settings sl@48: checkBoxReverseScreen.Checked = cds.ReverseScreen; sl@57: checkBoxInverseColors.Checked = cds.InverseColors; StephaneLenclud@115: checkBoxShowVolumeLabel.Checked = cds.ShowVolumeLabel; sl@100: checkBoxScaleToFit.Checked = cds.ScaleToFit; sl@100: maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit; sl@100: labelMinFontSize.Enabled = cds.ScaleToFit; sl@100: maskedTextBoxMinFontSize.Text = cds.MinFontSize.ToString(); StephaneLenclud@106: maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString(); sl@48: comboBoxDisplayType.SelectedIndex = cds.DisplayType; sl@48: timer.Interval = cds.TimerInterval; sl@48: maskedTextBoxTimerInterval.Text = cds.TimerInterval.ToString(); sl@100: textBoxScrollLoopSeparator.Text = cds.Separator; sl@58: // sl@58: SetupPixelDelegates(); sl@48: sl@7: if (iDisplay.IsOpen()) sl@7: { StephaneLenclud@187: //We have a display connection StephaneLenclud@187: //Reflect that in our UI StephaneLenclud@187: StartTimer(); StephaneLenclud@103: StephaneLenclud@175: iTableLayoutPanel.Enabled = true; StephaneLenclud@105: panelDisplay.Enabled = true; StephaneLenclud@103: sl@48: //Only setup brightness if display is open sl@48: trackBarBrightness.Minimum = iDisplay.MinBrightness(); sl@48: trackBarBrightness.Maximum = iDisplay.MaxBrightness(); StephaneLenclud@105: if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness()) StephaneLenclud@105: { StephaneLenclud@105: //Brightness out of range, this can occur when using auto-detect StephaneLenclud@105: //Use max brightness instead StephaneLenclud@105: trackBarBrightness.Value = iDisplay.MaxBrightness(); StephaneLenclud@105: iDisplay.SetBrightness(iDisplay.MaxBrightness()); StephaneLenclud@105: } StephaneLenclud@105: else StephaneLenclud@105: { StephaneLenclud@105: trackBarBrightness.Value = cds.Brightness; StephaneLenclud@105: iDisplay.SetBrightness(cds.Brightness); StephaneLenclud@105: } StephaneLenclud@105: StephaneLenclud@105: //Try compute the steps to something that makes sense sl@48: trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness()) / 5); sl@48: trackBarBrightness.SmallChange = 1; StephaneLenclud@105: sl@48: // sl@7: buttonFill.Enabled = true; sl@7: buttonClear.Enabled = true; sl@7: buttonOpen.Enabled = false; sl@7: buttonClose.Enabled = true; sl@7: trackBarBrightness.Enabled = true; sl@10: toolStripStatusLabelConnect.Text = "Connected - " + iDisplay.Vendor() + " - " + iDisplay.Product(); sl@10: //+ " - " + iDisplay.SerialNumber(); sl@52: sl@52: if (iDisplay.SupportPowerOnOff()) sl@52: { sl@52: buttonPowerOn.Enabled = true; sl@52: buttonPowerOff.Enabled = true; sl@52: } sl@52: else sl@52: { sl@52: buttonPowerOn.Enabled = false; sl@52: buttonPowerOff.Enabled = false; sl@52: } sl@53: sl@53: if (iDisplay.SupportClock()) sl@53: { sl@53: buttonShowClock.Enabled = true; sl@53: buttonHideClock.Enabled = true; sl@53: } sl@53: else sl@53: { sl@53: buttonShowClock.Enabled = false; sl@53: buttonHideClock.Enabled = false; sl@53: } StephaneLenclud@115: StephaneLenclud@115: StephaneLenclud@115: //Check if Volume Label is supported. To date only MDM166AA supports that crap :) StephaneLenclud@135: checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel)>0; StephaneLenclud@115: StephaneLenclud@115: if (cds.ShowVolumeLabel) StephaneLenclud@115: { StephaneLenclud@135: iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel); StephaneLenclud@115: } StephaneLenclud@115: else StephaneLenclud@115: { StephaneLenclud@135: iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel); StephaneLenclud@115: } sl@7: } sl@7: else sl@7: { StephaneLenclud@187: //Display connection not available StephaneLenclud@187: //Reflect that in our UI StephaneLenclud@187: #if DEBUG StephaneLenclud@187: //In debug start our timer even if we don't have a display connection StephaneLenclud@187: StartTimer(); StephaneLenclud@187: #else StephaneLenclud@187: //In production environment we don't need our timer if no display connection StephaneLenclud@187: StopTimer(); StephaneLenclud@187: #endif StephaneLenclud@187: checkBoxShowVolumeLabel.Enabled = false; StephaneLenclud@175: iTableLayoutPanel.Enabled = false; StephaneLenclud@105: panelDisplay.Enabled = false; sl@7: buttonFill.Enabled = false; sl@7: buttonClear.Enabled = false; sl@7: buttonOpen.Enabled = true; sl@7: buttonClose.Enabled = false; sl@7: trackBarBrightness.Enabled = false; sl@52: buttonPowerOn.Enabled = false; sl@52: buttonPowerOff.Enabled = false; sl@53: buttonShowClock.Enabled = false; sl@53: buttonHideClock.Enabled = false; sl@9: toolStripStatusLabelConnect.Text = "Disconnected"; sl@48: toolStripStatusLabelPower.Text = "N/A"; sl@7: } StephaneLenclud@106: sl@7: } sl@9: sl@13: StephaneLenclud@115: /// StephaneLenclud@115: /// StephaneLenclud@115: /// StephaneLenclud@115: /// StephaneLenclud@115: /// StephaneLenclud@115: private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e) StephaneLenclud@115: { StephaneLenclud@115: cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked; StephaneLenclud@115: Properties.Settings.Default.Save(); StephaneLenclud@115: UpdateStatus(); StephaneLenclud@115: } sl@13: sl@9: private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e) sl@9: { sl@16: //Save our show borders setting StephaneLenclud@175: iTableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked ? TableLayoutPanelCellBorderStyle.Single : TableLayoutPanelCellBorderStyle.None); sl@48: cds.ShowBorders = checkBoxShowBorders.Checked; sl@9: Properties.Settings.Default.Save(); sl@57: CheckFontHeight(); sl@9: } sl@13: sl@13: private void checkBoxConnectOnStartup_CheckedChanged(object sender, EventArgs e) sl@13: { sl@16: //Save our connect on startup setting sl@13: Properties.Settings.Default.DisplayConnectOnStartup = checkBoxConnectOnStartup.Checked; sl@13: Properties.Settings.Default.Save(); sl@13: } sl@13: sl@94: private void checkBoxMinimizeToTray_CheckedChanged(object sender, EventArgs e) sl@94: { sl@94: //Save our "Minimize to tray" setting sl@94: Properties.Settings.Default.MinimizeToTray = checkBoxMinimizeToTray.Checked; sl@94: Properties.Settings.Default.Save(); sl@94: sl@94: } sl@94: sl@94: private void checkBoxStartMinimized_CheckedChanged(object sender, EventArgs e) sl@94: { sl@94: //Save our "Start minimized" setting sl@94: Properties.Settings.Default.StartMinimized = checkBoxStartMinimized.Checked; sl@94: Properties.Settings.Default.Save(); sl@94: } sl@94: StephaneLenclud@194: private void checkBoxStartIdleClient_CheckedChanged(object sender, EventArgs e) StephaneLenclud@194: { StephaneLenclud@194: Properties.Settings.Default.StartIdleClient = iCheckBoxStartIdleClient.Checked; StephaneLenclud@194: Properties.Settings.Default.Save(); StephaneLenclud@194: } StephaneLenclud@194: StephaneLenclud@194: private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e) sl@94: { sl@94: iStartupManager.Startup = checkBoxAutoStart.Checked; sl@94: } sl@94: sl@94: sl@16: private void checkBoxReverseScreen_CheckedChanged(object sender, EventArgs e) sl@16: { sl@16: //Save our reverse screen setting sl@48: cds.ReverseScreen = checkBoxReverseScreen.Checked; sl@16: Properties.Settings.Default.Save(); sl@58: SetupPixelDelegates(); sl@16: } sl@16: sl@57: private void checkBoxInverseColors_CheckedChanged(object sender, EventArgs e) sl@57: { sl@57: //Save our inverse colors setting sl@57: cds.InverseColors = checkBoxInverseColors.Checked; sl@57: Properties.Settings.Default.Save(); sl@58: SetupPixelDelegates(); sl@57: } sl@57: sl@100: private void checkBoxScaleToFit_CheckedChanged(object sender, EventArgs e) sl@100: { sl@100: //Save our scale to fit setting sl@100: cds.ScaleToFit = checkBoxScaleToFit.Checked; sl@100: Properties.Settings.Default.Save(); sl@100: // sl@100: labelMinFontSize.Enabled = cds.ScaleToFit; sl@100: maskedTextBoxMinFontSize.Enabled = cds.ScaleToFit; sl@100: } sl@100: sl@14: private void MainForm_Resize(object sender, EventArgs e) sl@14: { sl@14: if (WindowState == FormWindowState.Minimized) sl@14: { StephaneLenclud@158: // To workaround our empty bitmap bug on Windows 7 we need to recreate our bitmap when the application is minimized StephaneLenclud@158: // That's apparently not needed on Windows 10 but we better leave it in place. sl@14: iCreateBitmap = true; sl@14: } sl@14: } sl@14: sl@17: private void MainForm_FormClosing(object sender, FormClosingEventArgs e) sl@17: { StephaneLenclud@167: iCecManager.Stop(); StephaneLenclud@117: iNetworkManager.Dispose(); StephaneLenclud@105: CloseDisplayConnection(); sl@17: StopServer(); sl@32: e.Cancel = iClosing; sl@17: } sl@17: sl@17: public void StartServer() sl@17: { sl@17: iServiceHost = new ServiceHost sl@17: ( sl@55: typeof(Session), sl@20: new Uri[] { new Uri("net.tcp://localhost:8001/") } sl@17: ); sl@17: sl@55: iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), "DisplayService"); sl@17: iServiceHost.Open(); sl@17: } sl@17: sl@17: public void StopServer() sl@17: { sl@32: if (iClients.Count > 0 && !iClosing) sl@29: { sl@29: //Tell our clients sl@32: iClosing = true; sl@29: BroadcastCloseEvent(); sl@29: } sl@32: else if (iClosing) sl@32: { sl@32: if (MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) sl@32: { sl@32: iClosing = false; //We make sure we force close if asked twice sl@32: } sl@32: } sl@32: else sl@36: { sl@32: //We removed that as it often lags for some reason sl@32: //iServiceHost.Close(); sl@32: } sl@17: } sl@17: sl@21: public void BroadcastCloseEvent() sl@21: { sl@31: Trace.TraceInformation("BroadcastCloseEvent - start"); sl@31: sl@21: var inactiveClients = new List(); sl@21: foreach (var client in iClients) sl@21: { sl@21: //if (client.Key != eventData.ClientName) sl@21: { sl@21: try sl@21: { sl@31: Trace.TraceInformation("BroadcastCloseEvent - " + client.Key); sl@33: client.Value.Callback.OnCloseOrder(/*eventData*/); sl@21: } sl@21: catch (Exception ex) sl@21: { sl@21: inactiveClients.Add(client.Key); sl@21: } sl@21: } sl@21: } sl@21: sl@21: if (inactiveClients.Count > 0) sl@21: { sl@21: foreach (var client in inactiveClients) sl@21: { sl@21: iClients.Remove(client); StephaneLenclud@188: Program.iMainForm.iTreeViewClients.Nodes.Remove(Program.iMainForm.iTreeViewClients.Nodes.Find(client, false)[0]); sl@21: } sl@21: } sl@97: sl@97: if (iClients.Count==0) sl@97: { sl@97: ClearLayout(); sl@97: } sl@21: } sl@21: sl@97: /// sl@97: /// Just remove all our fields. sl@97: /// sl@97: private void ClearLayout() sl@97: { StephaneLenclud@175: iTableLayoutPanel.Controls.Clear(); StephaneLenclud@175: iTableLayoutPanel.RowStyles.Clear(); StephaneLenclud@175: iTableLayoutPanel.ColumnStyles.Clear(); StephaneLenclud@122: iCurrentClientData = null; sl@97: } sl@97: StephaneLenclud@106: /// StephaneLenclud@106: /// Just launch a demo client. StephaneLenclud@106: /// StephaneLenclud@106: private void StartNewClient(string aTopText = "", string aBottomText = "") StephaneLenclud@106: { StephaneLenclud@106: Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams); StephaneLenclud@106: SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams(new Point(this.Right, this.Top),aTopText,aBottomText); StephaneLenclud@106: clientThread.Start(myParams); StephaneLenclud@106: BringToFront(); StephaneLenclud@106: } StephaneLenclud@106: StephaneLenclud@189: /// StephaneLenclud@189: /// Just launch our idle client. StephaneLenclud@189: /// StephaneLenclud@189: private void StartIdleClient(string aTopText = "", string aBottomText = "") StephaneLenclud@189: { StephaneLenclud@189: Thread clientThread = new Thread(SharpDisplayIdleClient.Program.MainWithParams); StephaneLenclud@189: SharpDisplayIdleClient.StartParams myParams = new SharpDisplayIdleClient.StartParams(new Point(this.Right, this.Top), aTopText, aBottomText); StephaneLenclud@189: clientThread.Start(myParams); StephaneLenclud@189: BringToFront(); StephaneLenclud@189: } StephaneLenclud@189: StephaneLenclud@189: sl@25: private void buttonStartClient_Click(object sender, EventArgs e) sl@25: { StephaneLenclud@106: StartNewClient(); sl@25: } sl@25: sl@27: private void buttonSuspend_Click(object sender, EventArgs e) sl@27: { StephaneLenclud@187: ToggleTimer(); StephaneLenclud@187: } StephaneLenclud@187: StephaneLenclud@187: private void StartTimer() StephaneLenclud@187: { StephaneLenclud@187: LastTickTime = DateTime.Now; //Reset timer to prevent jump StephaneLenclud@187: timer.Enabled = true; StephaneLenclud@187: UpdateSuspendButton(); StephaneLenclud@187: } StephaneLenclud@187: StephaneLenclud@187: private void StopTimer() StephaneLenclud@187: { StephaneLenclud@187: LastTickTime = DateTime.Now; //Reset timer to prevent jump StephaneLenclud@187: timer.Enabled = false; StephaneLenclud@187: UpdateSuspendButton(); StephaneLenclud@187: } StephaneLenclud@187: StephaneLenclud@187: private void ToggleTimer() StephaneLenclud@187: { sl@52: LastTickTime = DateTime.Now; //Reset timer to prevent jump sl@27: timer.Enabled = !timer.Enabled; StephaneLenclud@187: UpdateSuspendButton(); StephaneLenclud@187: } StephaneLenclud@187: StephaneLenclud@187: private void UpdateSuspendButton() StephaneLenclud@187: { sl@27: if (!timer.Enabled) sl@27: { sl@52: buttonSuspend.Text = "Run"; sl@27: } sl@27: else sl@27: { sl@27: buttonSuspend.Text = "Pause"; sl@27: } sl@27: } sl@27: StephaneLenclud@187: sl@29: private void buttonCloseClients_Click(object sender, EventArgs e) sl@29: { sl@29: BroadcastCloseEvent(); sl@29: } sl@29: sl@30: private void treeViewClients_AfterSelect(object sender, TreeViewEventArgs e) sl@30: { StephaneLenclud@141: //Root node must have at least one child StephaneLenclud@141: if (e.Node.Nodes.Count == 0) StephaneLenclud@141: { StephaneLenclud@141: return; StephaneLenclud@141: } sl@21: StephaneLenclud@141: //If the selected node is the root node of a client then switch to it StephaneLenclud@141: string sessionId=e.Node.Nodes[0].Text; //First child of a root node is the sessionId StephaneLenclud@141: if (iClients.ContainsKey(sessionId)) //Check that's actually what we are looking at StephaneLenclud@141: { StephaneLenclud@141: //We have a valid session just switch to that client StephaneLenclud@141: SetCurrentClient(sessionId,true); StephaneLenclud@141: } StephaneLenclud@141: sl@30: } sl@30: sl@36: sl@30: /// sl@36: /// sl@30: /// sl@30: /// sl@30: /// sl@55: public void AddClientThreadSafe(string aSessionId, ICallback aCallback) sl@30: { sl@33: if (this.InvokeRequired) sl@30: { sl@30: //Not in the proper thread, invoke ourselves sl@30: AddClientDelegate d = new AddClientDelegate(AddClientThreadSafe); sl@30: this.Invoke(d, new object[] { aSessionId, aCallback }); sl@30: } sl@30: else sl@30: { sl@30: //We are in the proper thread sl@30: //Add this session to our collection of clients sl@33: ClientData newClient = new ClientData(aSessionId, aCallback); sl@33: Program.iMainForm.iClients.Add(aSessionId, newClient); sl@30: //Add this session to our UI sl@33: UpdateClientTreeViewNode(newClient); sl@30: } sl@30: } sl@30: Stephane@186: Stephane@186: /// Stephane@186: /// Find the client with the highest priority if any. Stephane@186: /// Stephane@186: /// Our highest priority client or null if not a single client is connected. Stephane@186: public ClientData FindHighestPriorityClient() Stephane@186: { Stephane@186: ClientData highestPriorityClient = null; Stephane@186: foreach (var client in iClients) Stephane@186: { Stephane@186: if (highestPriorityClient == null || client.Value.Priority > highestPriorityClient.Priority) Stephane@186: { Stephane@186: highestPriorityClient = client.Value; Stephane@186: } Stephane@186: } Stephane@186: Stephane@186: return highestPriorityClient; Stephane@186: } Stephane@186: sl@30: /// sl@36: /// sl@30: /// sl@30: /// sl@30: public void RemoveClientThreadSafe(string aSessionId) sl@30: { sl@33: if (this.InvokeRequired) sl@30: { sl@30: //Not in the proper thread, invoke ourselves sl@30: RemoveClientDelegate d = new RemoveClientDelegate(RemoveClientThreadSafe); sl@30: this.Invoke(d, new object[] { aSessionId }); sl@30: } sl@30: else sl@30: { sl@30: //We are in the proper thread sl@33: //Remove this session from both client collection and UI tree view sl@30: if (Program.iMainForm.iClients.Keys.Contains(aSessionId)) sl@30: { sl@30: Program.iMainForm.iClients.Remove(aSessionId); StephaneLenclud@188: Program.iMainForm.iTreeViewClients.Nodes.Remove(Program.iMainForm.iTreeViewClients.Nodes.Find(aSessionId, false)[0]); StephaneLenclud@188: //Update recording status too whenever a client is removed StephaneLenclud@188: UpdateRecordingNotification(); sl@32: } sl@32: Stephane@186: if (iCurrentClientSessionId == aSessionId) Stephane@186: { Stephane@186: //The current client is closing Stephane@186: iCurrentClientData = null; Stephane@186: //Find the client with the highest priority and set it as current Stephane@186: ClientData newCurrentClient = FindHighestPriorityClient(); Stephane@186: if (newCurrentClient!=null) Stephane@186: { Stephane@186: SetCurrentClient(newCurrentClient.SessionId, true); Stephane@186: } Stephane@186: } Stephane@186: Stephane@186: if (iClients.Count == 0) sl@97: { sl@97: //Clear our screen when last client disconnects sl@97: ClearLayout(); sl@97: sl@97: if (iClosing) sl@97: { sl@97: //We were closing our form sl@97: //All clients are now closed sl@97: //Just resume our close operation sl@97: iClosing = false; sl@97: Close(); sl@97: } sl@97: } sl@30: } sl@30: } sl@30: sl@30: /// sl@36: /// sl@30: /// sl@62: /// sl@72: /// sl@62: public void SetClientLayoutThreadSafe(string aSessionId, TableLayout aLayout) sl@62: { sl@62: if (this.InvokeRequired) sl@62: { sl@62: //Not in the proper thread, invoke ourselves sl@62: SetLayoutDelegate d = new SetLayoutDelegate(SetClientLayoutThreadSafe); sl@62: this.Invoke(d, new object[] { aSessionId, aLayout }); sl@62: } sl@62: else sl@62: { sl@62: ClientData client = iClients[aSessionId]; sl@62: if (client != null) sl@62: { StephaneLenclud@148: //Don't change a thing if the layout is the same StephaneLenclud@148: if (!client.Layout.IsSameAs(aLayout)) StephaneLenclud@148: { StephaneLenclud@158: Debug.Print("SetClientLayoutThreadSafe: Layout updated."); StephaneLenclud@148: //Set our client layout then StephaneLenclud@148: client.Layout = aLayout; StephaneLenclud@175: //So that next time we update all our fields at ones StephaneLenclud@175: client.HasNewLayout = true; StephaneLenclud@158: //Layout has changed clear our fields then StephaneLenclud@158: client.Fields.Clear(); StephaneLenclud@148: // StephaneLenclud@148: UpdateClientTreeViewNode(client); StephaneLenclud@148: } StephaneLenclud@158: else StephaneLenclud@158: { StephaneLenclud@158: Debug.Print("SetClientLayoutThreadSafe: Layout has not changed."); StephaneLenclud@158: } sl@62: } sl@62: } sl@62: } sl@62: sl@62: /// sl@62: /// sl@62: /// sl@67: /// sl@72: /// sl@75: public void SetClientFieldThreadSafe(string aSessionId, DataField aField) sl@30: { sl@33: if (this.InvokeRequired) sl@30: { sl@30: //Not in the proper thread, invoke ourselves sl@79: SetFieldDelegate d = new SetFieldDelegate(SetClientFieldThreadSafe); sl@72: this.Invoke(d, new object[] { aSessionId, aField }); sl@30: } sl@30: else sl@30: { sl@75: //We are in the proper thread sl@75: //Call the non-thread-safe variant sl@75: SetClientField(aSessionId, aField); sl@75: } sl@75: } sl@75: StephaneLenclud@176: StephaneLenclud@176: StephaneLenclud@176: sl@75: /// StephaneLenclud@175: /// Set a data field in the given client. sl@75: /// sl@75: /// sl@75: /// sl@75: private void SetClientField(string aSessionId, DataField aField) StephaneLenclud@141: { StephaneLenclud@141: //TODO: should check if the field actually changed? StephaneLenclud@141: sl@75: ClientData client = iClients[aSessionId]; StephaneLenclud@141: bool layoutChanged = false; StephaneLenclud@148: bool contentChanged = true; StephaneLenclud@141: StephaneLenclud@176: //Fetch our field index StephaneLenclud@176: int fieldIndex = client.FindSameFieldIndex(aField); StephaneLenclud@176: StephaneLenclud@176: if (fieldIndex < 0) sl@75: { StephaneLenclud@176: //No corresponding field, just bail out StephaneLenclud@176: return; StephaneLenclud@141: } sl@76: StephaneLenclud@175: //Keep our previous field in there StephaneLenclud@176: DataField previousField = client.Fields[fieldIndex]; StephaneLenclud@176: //Just update that field then StephaneLenclud@176: client.Fields[fieldIndex] = aField; StephaneLenclud@148: StephaneLenclud@176: if (!aField.IsTableField) StephaneLenclud@176: { StephaneLenclud@176: //We are done then if that field is not in our table layout StephaneLenclud@176: return; StephaneLenclud@176: } StephaneLenclud@176: StephaneLenclud@176: TableField tableField = (TableField) aField; StephaneLenclud@172: StephaneLenclud@175: if (previousField.IsSameLayout(aField)) StephaneLenclud@141: { StephaneLenclud@141: //If we are updating a field in our current client we need to update it in our panel StephaneLenclud@141: if (aSessionId == iCurrentClientSessionId) sl@30: { StephaneLenclud@176: Control ctrl=iTableLayoutPanel.GetControlFromPosition(tableField.Column, tableField.Row); StephaneLenclud@176: if (aField.IsTextField && ctrl is MarqueeLabel) sl@79: { StephaneLenclud@172: TextField textField=(TextField)aField; sl@75: //Text field control already in place, just change the text StephaneLenclud@176: MarqueeLabel label = (MarqueeLabel)ctrl; StephaneLenclud@172: contentChanged = (label.Text != textField.Text || label.TextAlign != textField.Alignment); StephaneLenclud@172: label.Text = textField.Text; StephaneLenclud@172: label.TextAlign = textField.Alignment; sl@68: } StephaneLenclud@176: else if (aField.IsBitmapField && ctrl is PictureBox) sl@75: { StephaneLenclud@172: BitmapField bitmapField = (BitmapField)aField; StephaneLenclud@148: contentChanged = true; //TODO: Bitmap comp or should we leave that to clients? sl@75: //Bitmap field control already in place just change the bitmap StephaneLenclud@176: PictureBox pictureBox = (PictureBox)ctrl; StephaneLenclud@172: pictureBox.Image = bitmapField.Bitmap; sl@75: } sl@68: else sl@68: { StephaneLenclud@141: layoutChanged = true; sl@68: } sl@30: } StephaneLenclud@141: } StephaneLenclud@141: else StephaneLenclud@148: { StephaneLenclud@141: layoutChanged = true; StephaneLenclud@141: } StephaneLenclud@141: StephaneLenclud@148: //If either content or layout changed we need to update our tree view to reflect the changes StephaneLenclud@148: if (contentChanged || layoutChanged) StephaneLenclud@141: { StephaneLenclud@141: UpdateClientTreeViewNode(client); StephaneLenclud@148: // StephaneLenclud@148: if (layoutChanged) sl@75: { StephaneLenclud@148: Debug.Print("Layout changed"); StephaneLenclud@148: //Our layout has changed, if we are already the current client we need to update our panel StephaneLenclud@148: if (aSessionId == iCurrentClientSessionId) StephaneLenclud@148: { StephaneLenclud@148: //Apply layout and set data fields. StephaneLenclud@148: UpdateTableLayoutPanel(iCurrentClientData); StephaneLenclud@148: } sl@75: } StephaneLenclud@158: else StephaneLenclud@158: { StephaneLenclud@158: Debug.Print("Layout has not changed."); StephaneLenclud@158: } StephaneLenclud@158: } StephaneLenclud@158: else StephaneLenclud@158: { StephaneLenclud@158: Debug.Print("WARNING: content and layout have not changed!"); StephaneLenclud@141: } sl@75: StephaneLenclud@141: //When a client field is set we try switching to this client to present the new information to our user StephaneLenclud@141: SetCurrentClient(aSessionId); sl@30: } sl@30: sl@30: /// sl@36: /// sl@30: /// sl@30: /// sl@75: public void SetClientFieldsThreadSafe(string aSessionId, System.Collections.Generic.IList aFields) sl@30: { sl@33: if (this.InvokeRequired) sl@30: { sl@30: //Not in the proper thread, invoke ourselves sl@75: SetFieldsDelegate d = new SetFieldsDelegate(SetClientFieldsThreadSafe); sl@72: this.Invoke(d, new object[] { aSessionId, aFields }); sl@30: } sl@30: else sl@30: { StephaneLenclud@175: ClientData client = iClients[aSessionId]; StephaneLenclud@175: StephaneLenclud@175: if (client.HasNewLayout) sl@30: { StephaneLenclud@175: //TODO: Assert client.Count == 0 StephaneLenclud@175: //Our layout was just changed StephaneLenclud@175: //Do some special handling to avoid re-creating our panel N times, once for each fields StephaneLenclud@175: client.HasNewLayout = false; StephaneLenclud@175: //Just set all our fields then StephaneLenclud@175: client.Fields.AddRange(aFields); StephaneLenclud@175: //Try switch to that client StephaneLenclud@175: SetCurrentClient(aSessionId); StephaneLenclud@175: StephaneLenclud@175: //If we are updating the current client update our panel StephaneLenclud@175: if (aSessionId == iCurrentClientSessionId) StephaneLenclud@175: { StephaneLenclud@175: //Apply layout and set data fields. StephaneLenclud@175: UpdateTableLayoutPanel(iCurrentClientData); StephaneLenclud@175: } StephaneLenclud@176: StephaneLenclud@176: UpdateClientTreeViewNode(client); StephaneLenclud@175: } StephaneLenclud@175: else StephaneLenclud@175: { StephaneLenclud@175: //Put each our text fields in a label control StephaneLenclud@175: foreach (DataField field in aFields) StephaneLenclud@175: { StephaneLenclud@175: SetClientField(aSessionId, field); StephaneLenclud@175: } sl@30: } sl@30: } sl@32: } sl@30: sl@67: /// sl@67: /// sl@67: /// sl@67: /// sl@32: /// sl@32: public void SetClientNameThreadSafe(string aSessionId, string aName) sl@32: { sl@32: if (this.InvokeRequired) sl@32: { sl@32: //Not in the proper thread, invoke ourselves sl@32: SetClientNameDelegate d = new SetClientNameDelegate(SetClientNameThreadSafe); sl@32: this.Invoke(d, new object[] { aSessionId, aName }); sl@32: } sl@32: else sl@32: { sl@32: //We are in the proper thread sl@33: //Get our client sl@33: ClientData client = iClients[aSessionId]; sl@33: if (client != null) sl@32: { sl@33: //Set its name sl@33: client.Name = aName; sl@33: //Update our tree-view sl@33: UpdateClientTreeViewNode(client); sl@33: } sl@33: } sl@33: } sl@33: StephaneLenclud@184: /// StephaneLenclud@184: public void SetClientPriorityThreadSafe(string aSessionId, uint aPriority) StephaneLenclud@184: { StephaneLenclud@184: if (this.InvokeRequired) StephaneLenclud@184: { StephaneLenclud@184: //Not in the proper thread, invoke ourselves StephaneLenclud@184: SetClientPriorityDelegate d = new SetClientPriorityDelegate(SetClientPriorityThreadSafe); StephaneLenclud@184: this.Invoke(d, new object[] { aSessionId, aPriority }); StephaneLenclud@184: } StephaneLenclud@184: else StephaneLenclud@184: { StephaneLenclud@184: //We are in the proper thread StephaneLenclud@184: //Get our client StephaneLenclud@184: ClientData client = iClients[aSessionId]; StephaneLenclud@184: if (client != null) StephaneLenclud@184: { StephaneLenclud@184: //Set its name StephaneLenclud@184: client.Priority = aPriority; StephaneLenclud@184: //Update our tree-view StephaneLenclud@184: UpdateClientTreeViewNode(client); Stephane@186: //Change our current client as per new priority Stephane@186: ClientData newCurrentClient = FindHighestPriorityClient(); Stephane@186: if (newCurrentClient!=null) Stephane@186: { Stephane@186: SetCurrentClient(newCurrentClient.SessionId); Stephane@186: } StephaneLenclud@184: } StephaneLenclud@184: } StephaneLenclud@184: } StephaneLenclud@184: sl@33: /// StephaneLenclud@183: /// StephaneLenclud@183: /// StephaneLenclud@183: /// StephaneLenclud@183: /// StephaneLenclud@183: /// StephaneLenclud@183: public static string Truncate(string value, int maxChars) StephaneLenclud@183: { StephaneLenclud@183: return value.Length <= maxChars ? value : value.Substring(0, maxChars-3) + "..."; StephaneLenclud@183: } StephaneLenclud@183: StephaneLenclud@183: /// StephaneLenclud@180: /// Update our recording notification. StephaneLenclud@180: /// StephaneLenclud@180: private void UpdateRecordingNotification() StephaneLenclud@180: { StephaneLenclud@180: //Go through each StephaneLenclud@180: bool activeRecording = false; StephaneLenclud@180: string text=""; StephaneLenclud@180: RecordingField recField=new RecordingField(); StephaneLenclud@180: foreach (var client in iClients) StephaneLenclud@180: { StephaneLenclud@180: RecordingField rec=(RecordingField)client.Value.FindSameFieldAs(recField); StephaneLenclud@180: if (rec!=null && rec.IsActive) StephaneLenclud@180: { StephaneLenclud@180: activeRecording = true; StephaneLenclud@183: //Don't break cause we are collecting the names/texts. StephaneLenclud@183: if (!String.IsNullOrEmpty(rec.Text)) StephaneLenclud@183: { StephaneLenclud@183: text += (rec.Text + "\n"); StephaneLenclud@183: } StephaneLenclud@183: else StephaneLenclud@183: { StephaneLenclud@183: //Not text for that recording, use client name instead StephaneLenclud@183: text += client.Value.Name + " recording\n"; StephaneLenclud@183: } StephaneLenclud@183: StephaneLenclud@180: } StephaneLenclud@180: } StephaneLenclud@180: StephaneLenclud@183: //Update our text no matter what, can't have more than 63 characters otherwise it throws an exception. StephaneLenclud@183: iRecordingNotification.Text = Truncate(text,63); StephaneLenclud@183: StephaneLenclud@180: //Change visibility of notification if needed StephaneLenclud@180: if (iRecordingNotification.Visible != activeRecording) StephaneLenclud@183: { StephaneLenclud@180: iRecordingNotification.Visible = activeRecording; Stephane@182: //Assuming the notification icon is in sync with our display icon Stephane@182: //Take care of our REC icon StephaneLenclud@183: if (iDisplay.IsOpen()) StephaneLenclud@183: { StephaneLenclud@183: iDisplay.SetIconOnOff(MiniDisplay.IconType.Recording, activeRecording); StephaneLenclud@183: } StephaneLenclud@180: } StephaneLenclud@180: } StephaneLenclud@180: StephaneLenclud@180: /// sl@36: /// sl@33: /// sl@33: /// sl@33: private void UpdateClientTreeViewNode(ClientData aClient) sl@33: { StephaneLenclud@148: Debug.Print("UpdateClientTreeViewNode"); StephaneLenclud@148: sl@33: if (aClient == null) sl@33: { sl@33: return; sl@33: } sl@33: StephaneLenclud@180: //Hook in record icon update too StephaneLenclud@180: UpdateRecordingNotification(); StephaneLenclud@180: sl@33: TreeNode node = null; sl@33: //Check that our client node already exists sl@33: //Get our client root node using its key which is our session ID StephaneLenclud@188: TreeNode[] nodes = iTreeViewClients.Nodes.Find(aClient.SessionId, false); sl@33: if (nodes.Count()>0) sl@33: { sl@33: //We already have a node for that client sl@33: node = nodes[0]; sl@33: //Clear children as we are going to recreate them below sl@33: node.Nodes.Clear(); sl@33: } sl@33: else sl@33: { sl@33: //Client node does not exists create a new one StephaneLenclud@188: iTreeViewClients.Nodes.Add(aClient.SessionId, aClient.SessionId); StephaneLenclud@188: node = iTreeViewClients.Nodes.Find(aClient.SessionId, false)[0]; sl@33: } sl@33: sl@33: if (node != null) sl@33: { sl@33: //Change its name StephaneLenclud@184: if (!String.IsNullOrEmpty(aClient.Name)) sl@33: { StephaneLenclud@184: //We have a name, use it as text for our root node sl@33: node.Text = aClient.Name; sl@32: //Add a child with SessionId sl@33: node.Nodes.Add(new TreeNode(aClient.SessionId)); sl@33: } sl@33: else sl@33: { sl@33: //No name, use session ID instead sl@33: node.Text = aClient.SessionId; sl@33: } sl@36: StephaneLenclud@184: //Display client priority StephaneLenclud@184: node.Nodes.Add(new TreeNode("Priority: " + aClient.Priority)); StephaneLenclud@184: sl@67: if (aClient.Fields.Count > 0) sl@33: { sl@33: //Create root node for our texts sl@70: TreeNode textsRoot = new TreeNode("Fields"); sl@33: node.Nodes.Add(textsRoot); sl@33: //For each text add a new entry sl@67: foreach (DataField field in aClient.Fields) sl@33: { StephaneLenclud@172: if (field.IsTextField) sl@67: { StephaneLenclud@172: TextField textField = (TextField)field; sl@70: textsRoot.Nodes.Add(new TreeNode("[Text]" + textField.Text)); sl@67: } StephaneLenclud@172: else if (field.IsBitmapField) sl@67: { sl@72: textsRoot.Nodes.Add(new TreeNode("[Bitmap]")); sl@70: } StephaneLenclud@172: else if (field.IsRecordingField) StephaneLenclud@172: { StephaneLenclud@177: RecordingField recordingField = (RecordingField)field; StephaneLenclud@177: textsRoot.Nodes.Add(new TreeNode("[Recording]" + recordingField.IsActive)); StephaneLenclud@172: } sl@33: } sl@32: } sl@34: sl@34: node.ExpandAll(); sl@32: } sl@30: } sl@17: sl@60: /// sl@60: /// Update our table layout row styles to make sure each rows have similar height sl@60: /// sl@60: private void UpdateTableLayoutRowStyles() sl@60: { StephaneLenclud@175: foreach (RowStyle rowStyle in iTableLayoutPanel.RowStyles) sl@60: { sl@60: rowStyle.SizeType = SizeType.Percent; StephaneLenclud@175: rowStyle.Height = 100 / iTableLayoutPanel.RowCount; sl@60: } sl@60: } sl@60: sl@63: /// sl@63: /// Update our display table layout. StephaneLenclud@175: /// Will instanciated every field control as defined by our client. StephaneLenclud@175: /// Fields must be specified by rows from the left. sl@63: /// sl@63: /// sl@65: private void UpdateTableLayoutPanel(ClientData aClient) sl@63: { StephaneLenclud@175: Debug.Print("UpdateTableLayoutPanel"); StephaneLenclud@148: StephaneLenclud@106: if (aClient == null) StephaneLenclud@106: { StephaneLenclud@106: //Just drop it StephaneLenclud@106: return; StephaneLenclud@106: } StephaneLenclud@106: StephaneLenclud@106: sl@65: TableLayout layout = aClient.Layout; sl@70: StephaneLenclud@141: //First clean our current panel StephaneLenclud@175: iTableLayoutPanel.Controls.Clear(); StephaneLenclud@175: iTableLayoutPanel.RowStyles.Clear(); StephaneLenclud@175: iTableLayoutPanel.ColumnStyles.Clear(); StephaneLenclud@175: iTableLayoutPanel.RowCount = 0; StephaneLenclud@175: iTableLayoutPanel.ColumnCount = 0; sl@63: StephaneLenclud@175: //Then recreate our rows... StephaneLenclud@175: while (iTableLayoutPanel.RowCount < layout.Rows.Count) sl@63: { StephaneLenclud@175: iTableLayoutPanel.RowCount++; sl@63: } sl@63: StephaneLenclud@175: // ...and columns StephaneLenclud@175: while (iTableLayoutPanel.ColumnCount < layout.Columns.Count) sl@63: { StephaneLenclud@175: iTableLayoutPanel.ColumnCount++; sl@63: } sl@63: StephaneLenclud@175: //For each column StephaneLenclud@175: for (int i = 0; i < iTableLayoutPanel.ColumnCount; i++) sl@63: { sl@63: //Create our column styles StephaneLenclud@175: this.iTableLayoutPanel.ColumnStyles.Add(layout.Columns[i]); sl@63: StephaneLenclud@175: //For each rows StephaneLenclud@175: for (int j = 0; j < iTableLayoutPanel.RowCount; j++) sl@63: { sl@63: if (i == 0) sl@63: { sl@63: //Create our row styles StephaneLenclud@175: this.iTableLayoutPanel.RowStyles.Add(layout.Rows[j]); sl@63: } StephaneLenclud@176: else sl@70: { sl@70: continue; sl@70: } sl@63: } sl@63: } sl@63: StephaneLenclud@176: //For each field StephaneLenclud@176: foreach (DataField field in aClient.Fields) sl@70: { StephaneLenclud@176: if (!field.IsTableField) StephaneLenclud@176: { StephaneLenclud@176: //That field is not taking part in our table layout skip it StephaneLenclud@176: continue; StephaneLenclud@176: } StephaneLenclud@176: StephaneLenclud@176: TableField tableField = (TableField)field; StephaneLenclud@176: StephaneLenclud@176: //Create a control corresponding to the field specified for that cell StephaneLenclud@176: Control control = CreateControlForDataField(tableField); StephaneLenclud@176: StephaneLenclud@176: //Add newly created control to our table layout at the specified row and column StephaneLenclud@176: iTableLayoutPanel.Controls.Add(control, tableField.Column, tableField.Row); StephaneLenclud@176: //Make sure we specify column and row span for that new control StephaneLenclud@176: iTableLayoutPanel.SetColumnSpan(control, tableField.ColumnSpan); StephaneLenclud@176: iTableLayoutPanel.SetRowSpan(control, tableField.RowSpan); sl@70: } sl@70: StephaneLenclud@176: sl@63: CheckFontHeight(); sl@63: } sl@63: sl@68: /// sl@70: /// Check our type of data field and create corresponding control sl@68: /// sl@68: /// sl@69: private Control CreateControlForDataField(DataField aField) sl@68: { sl@68: Control control=null; StephaneLenclud@172: if (aField.IsTextField) sl@68: { sl@68: MarqueeLabel label = new SharpDisplayManager.MarqueeLabel(); sl@68: label.AutoEllipsis = true; sl@68: label.AutoSize = true; sl@68: label.BackColor = System.Drawing.Color.Transparent; sl@68: label.Dock = System.Windows.Forms.DockStyle.Fill; sl@68: label.Location = new System.Drawing.Point(1, 1); sl@68: label.Margin = new System.Windows.Forms.Padding(0); StephaneLenclud@176: label.Name = "marqueeLabel" + aField; sl@68: label.OwnTimer = false; StephaneLenclud@106: label.PixelsPerSecond = cds.ScrollingSpeedInPixelsPerSecond; sl@100: label.Separator = cds.Separator; sl@100: label.MinFontSize = cds.MinFontSize; sl@100: label.ScaleToFit = cds.ScaleToFit; sl@68: //control.Size = new System.Drawing.Size(254, 30); sl@68: //control.TabIndex = 2; sl@68: label.Font = cds.Font; sl@68: StephaneLenclud@172: TextField field = (TextField)aField; StephaneLenclud@172: label.TextAlign = field.Alignment; sl@68: label.UseCompatibleTextRendering = true; StephaneLenclud@172: label.Text = field.Text; sl@68: // sl@68: control = label; sl@68: } StephaneLenclud@172: else if (aField.IsBitmapField) sl@68: { sl@68: //Create picture box sl@68: PictureBox picture = new PictureBox(); sl@68: picture.AutoSize = true; sl@68: picture.BackColor = System.Drawing.Color.Transparent; sl@68: picture.Dock = System.Windows.Forms.DockStyle.Fill; sl@68: picture.Location = new System.Drawing.Point(1, 1); sl@68: picture.Margin = new System.Windows.Forms.Padding(0); sl@68: picture.Name = "pictureBox" + aField; sl@68: //Set our image StephaneLenclud@172: BitmapField field = (BitmapField)aField; StephaneLenclud@172: picture.Image = field.Bitmap; sl@68: // sl@68: control = picture; sl@68: } StephaneLenclud@172: //TODO: Handle recording field? sl@68: sl@69: return control; sl@68: } sl@68: StephaneLenclud@103: /// StephaneLenclud@103: /// Called when the user selected a new display type. StephaneLenclud@103: /// StephaneLenclud@103: /// StephaneLenclud@103: /// sl@46: private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e) sl@46: { StephaneLenclud@103: //Store the selected display type in our settings sl@48: Properties.Settings.Default.CurrentDisplayIndex = comboBoxDisplayType.SelectedIndex; sl@48: cds.DisplayType = comboBoxDisplayType.SelectedIndex; sl@46: Properties.Settings.Default.Save(); StephaneLenclud@103: StephaneLenclud@103: //Try re-opening the display connection if we were already connected. StephaneLenclud@103: //Otherwise just update our status to reflect display type change. sl@51: if (iDisplay.IsOpen()) sl@51: { sl@51: OpenDisplayConnection(); sl@51: } sl@51: else sl@51: { sl@51: UpdateStatus(); sl@51: } sl@46: } sl@46: sl@47: private void maskedTextBoxTimerInterval_TextChanged(object sender, EventArgs e) sl@47: { sl@47: if (maskedTextBoxTimerInterval.Text != "") sl@47: { sl@51: int interval = Convert.ToInt32(maskedTextBoxTimerInterval.Text); sl@51: sl@51: if (interval > 0) sl@51: { sl@51: timer.Interval = interval; sl@51: cds.TimerInterval = timer.Interval; sl@51: Properties.Settings.Default.Save(); sl@51: } sl@47: } sl@47: } sl@47: sl@100: private void maskedTextBoxMinFontSize_TextChanged(object sender, EventArgs e) sl@100: { sl@100: if (maskedTextBoxMinFontSize.Text != "") sl@100: { sl@100: int minFontSize = Convert.ToInt32(maskedTextBoxMinFontSize.Text); sl@100: sl@100: if (minFontSize > 0) sl@100: { sl@100: cds.MinFontSize = minFontSize; sl@100: Properties.Settings.Default.Save(); StephaneLenclud@106: //We need to recreate our layout for that change to take effect StephaneLenclud@106: UpdateTableLayoutPanel(iCurrentClientData); sl@100: } sl@100: } sl@100: } sl@100: StephaneLenclud@106: StephaneLenclud@106: private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e) StephaneLenclud@106: { StephaneLenclud@106: if (maskedTextBoxScrollingSpeed.Text != "") StephaneLenclud@106: { StephaneLenclud@106: int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text); StephaneLenclud@106: StephaneLenclud@106: if (scrollingSpeed > 0) StephaneLenclud@106: { StephaneLenclud@106: cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed; StephaneLenclud@106: Properties.Settings.Default.Save(); StephaneLenclud@106: //We need to recreate our layout for that change to take effect StephaneLenclud@106: UpdateTableLayoutPanel(iCurrentClientData); StephaneLenclud@106: } StephaneLenclud@106: } StephaneLenclud@106: } StephaneLenclud@106: sl@100: private void textBoxScrollLoopSeparator_TextChanged(object sender, EventArgs e) sl@100: { sl@100: cds.Separator = textBoxScrollLoopSeparator.Text; sl@100: Properties.Settings.Default.Save(); StephaneLenclud@110: StephaneLenclud@110: //Update our text fields StephaneLenclud@175: foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls) StephaneLenclud@110: { StephaneLenclud@110: ctrl.Separator = cds.Separator; StephaneLenclud@110: } StephaneLenclud@110: sl@100: } sl@100: sl@52: private void buttonPowerOn_Click(object sender, EventArgs e) sl@52: { sl@52: iDisplay.PowerOn(); sl@52: } sl@52: sl@52: private void buttonPowerOff_Click(object sender, EventArgs e) sl@52: { sl@52: iDisplay.PowerOff(); sl@52: } sl@52: sl@53: private void buttonShowClock_Click(object sender, EventArgs e) sl@53: { StephaneLenclud@122: ShowClock(); sl@53: } sl@53: sl@53: private void buttonHideClock_Click(object sender, EventArgs e) sl@53: { StephaneLenclud@122: HideClock(); sl@53: } sl@88: sl@88: private void buttonUpdate_Click(object sender, EventArgs e) sl@88: { sl@88: InstallUpdateSyncWithInfo(); sl@88: } sl@88: StephaneLenclud@122: /// StephaneLenclud@122: /// StephaneLenclud@122: /// StephaneLenclud@122: void ShowClock() StephaneLenclud@122: { StephaneLenclud@122: if (!iDisplay.IsOpen()) StephaneLenclud@122: { StephaneLenclud@122: return; StephaneLenclud@122: } StephaneLenclud@122: StephaneLenclud@122: //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock StephaneLenclud@122: iSkipFrameRendering = true; StephaneLenclud@122: //Clear our screen StephaneLenclud@122: iDisplay.Clear(); StephaneLenclud@122: iDisplay.SwapBuffers(); StephaneLenclud@122: //Then show our clock StephaneLenclud@122: iDisplay.ShowClock(); StephaneLenclud@122: } StephaneLenclud@122: StephaneLenclud@122: /// StephaneLenclud@122: /// StephaneLenclud@122: /// StephaneLenclud@122: void HideClock() StephaneLenclud@122: { StephaneLenclud@122: if (!iDisplay.IsOpen()) StephaneLenclud@122: { StephaneLenclud@122: return; StephaneLenclud@122: } StephaneLenclud@122: StephaneLenclud@122: //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock StephaneLenclud@122: iSkipFrameRendering = false; StephaneLenclud@122: iDisplay.HideClock(); StephaneLenclud@122: } sl@88: sl@88: private void InstallUpdateSyncWithInfo() sl@88: { sl@88: UpdateCheckInfo info = null; sl@88: sl@88: if (ApplicationDeployment.IsNetworkDeployed) sl@88: { sl@88: ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment; sl@88: sl@88: try sl@88: { sl@88: info = ad.CheckForDetailedUpdate(); sl@88: sl@88: } sl@88: catch (DeploymentDownloadException dde) sl@88: { sl@88: 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); sl@88: return; sl@88: } sl@88: catch (InvalidDeploymentException ide) sl@88: { sl@88: 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); sl@88: return; sl@88: } sl@88: catch (InvalidOperationException ioe) sl@88: { sl@88: MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message); sl@88: return; sl@88: } sl@88: sl@90: if (info.UpdateAvailable) sl@90: { sl@90: Boolean doUpdate = true; sl@88: sl@90: if (!info.IsUpdateRequired) sl@90: { sl@90: DialogResult dr = MessageBox.Show("An update is available. Would you like to update the application now?", "Update Available", MessageBoxButtons.OKCancel); sl@90: if (!(DialogResult.OK == dr)) sl@90: { sl@90: doUpdate = false; sl@90: } sl@90: } sl@90: else sl@90: { StephaneLenclud@187: // Display a message that the application MUST reboot. Display the minimum required version. sl@90: MessageBox.Show("This application has detected a mandatory update from your current " + sl@90: "version to version " + info.MinimumRequiredVersion.ToString() + sl@90: ". The application will now install the update and restart.", sl@90: "Update Available", MessageBoxButtons.OK, sl@90: MessageBoxIcon.Information); sl@90: } sl@88: sl@90: if (doUpdate) sl@90: { sl@90: try sl@90: { sl@90: ad.Update(); sl@90: MessageBox.Show("The application has been upgraded, and will now restart."); sl@90: Application.Restart(); sl@90: } sl@90: catch (DeploymentDownloadException dde) sl@90: { sl@90: MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde); sl@90: return; sl@90: } sl@90: } sl@90: } sl@90: else sl@90: { sl@90: MessageBox.Show("You are already running the latest version.", "Application up-to-date"); sl@90: } sl@88: } sl@88: } sl@92: sl@94: sl@94: /// sl@99: /// Used to sl@94: /// sl@94: private void SysTrayHideShow() sl@92: { sl@94: Visible = !Visible; sl@94: if (Visible) sl@94: { sl@94: Activate(); sl@94: WindowState = FormWindowState.Normal; sl@94: } sl@92: } sl@94: sl@94: /// sl@94: /// Use to handle minimize events. sl@94: /// sl@94: /// sl@94: /// sl@94: private void MainForm_SizeChanged(object sender, EventArgs e) sl@94: { sl@94: if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray) sl@94: { sl@94: if (Visible) sl@94: { sl@94: SysTrayHideShow(); sl@94: } sl@94: } sl@94: } sl@94: StephaneLenclud@105: /// StephaneLenclud@105: /// StephaneLenclud@105: /// StephaneLenclud@105: /// StephaneLenclud@105: /// StephaneLenclud@105: private void tableLayoutPanel_SizeChanged(object sender, EventArgs e) StephaneLenclud@105: { StephaneLenclud@105: //Our table layout size has changed which means our display size has changed. StephaneLenclud@105: //We need to re-create our bitmap. StephaneLenclud@105: iCreateBitmap = true; StephaneLenclud@105: } StephaneLenclud@126: StephaneLenclud@126: /// StephaneLenclud@126: /// StephaneLenclud@126: /// StephaneLenclud@126: /// StephaneLenclud@126: /// StephaneLenclud@126: private void buttonSelectFile_Click(object sender, EventArgs e) StephaneLenclud@126: { StephaneLenclud@126: //openFileDialog1.InitialDirectory = "c:\\"; StephaneLenclud@126: //openFileDialog.Filter = "EXE files (*.exe)|*.exe|All files (*.*)|*.*"; StephaneLenclud@126: //openFileDialog.FilterIndex = 1; StephaneLenclud@126: openFileDialog.RestoreDirectory = true; StephaneLenclud@126: StephaneLenclud@126: if (DlgBox.ShowDialog(openFileDialog) == DialogResult.OK) StephaneLenclud@126: { StephaneLenclud@126: labelStartFileName.Text = openFileDialog.FileName; StephaneLenclud@126: Properties.Settings.Default.StartFileName = openFileDialog.FileName; StephaneLenclud@126: Properties.Settings.Default.Save(); StephaneLenclud@126: } StephaneLenclud@126: } StephaneLenclud@153: StephaneLenclud@153: /// StephaneLenclud@153: /// StephaneLenclud@153: /// StephaneLenclud@153: /// StephaneLenclud@153: /// StephaneLenclud@153: private void comboBoxOpticalDrives_SelectedIndexChanged(object sender, EventArgs e) StephaneLenclud@153: { StephaneLenclud@153: //Save the optical drive the user selected for ejection StephaneLenclud@153: Properties.Settings.Default.OpticalDriveToEject = comboBoxOpticalDrives.SelectedItem.ToString(); StephaneLenclud@153: Properties.Settings.Default.Save(); StephaneLenclud@153: } StephaneLenclud@167: StephaneLenclud@206: StephaneLenclud@206: /// StephaneLenclud@206: /// StephaneLenclud@206: /// StephaneLenclud@206: private void LogsUpdate() StephaneLenclud@206: { StephaneLenclud@206: if (iWriter != null) StephaneLenclud@206: { StephaneLenclud@206: iWriter.Flush(); StephaneLenclud@206: } StephaneLenclud@206: StephaneLenclud@206: } StephaneLenclud@206: StephaneLenclud@167: /// StephaneLenclud@167: /// Broadcast messages to subscribers. StephaneLenclud@167: /// StephaneLenclud@167: /// StephaneLenclud@167: protected override void WndProc(ref Message aMessage) StephaneLenclud@167: { StephaneLenclud@206: LogsUpdate(); Stephane@202: StephaneLenclud@167: if (OnWndProc!=null) StephaneLenclud@167: { StephaneLenclud@167: OnWndProc(ref aMessage); StephaneLenclud@167: } StephaneLenclud@167: StephaneLenclud@167: base.WndProc(ref aMessage); StephaneLenclud@167: } StephaneLenclud@168: StephaneLenclud@168: private void checkBoxCecEnabled_CheckedChanged(object sender, EventArgs e) StephaneLenclud@168: { StephaneLenclud@168: //Save CEC enabled status StephaneLenclud@168: Properties.Settings.Default.CecEnabled = checkBoxCecEnabled.Checked; StephaneLenclud@168: Properties.Settings.Default.Save(); StephaneLenclud@168: // StephaneLenclud@168: ResetCec(); StephaneLenclud@168: } StephaneLenclud@168: StephaneLenclud@168: private void comboBoxHdmiPort_SelectedIndexChanged(object sender, EventArgs e) StephaneLenclud@168: { StephaneLenclud@168: //Save CEC HDMI port StephaneLenclud@168: Properties.Settings.Default.CecHdmiPort = Convert.ToByte(comboBoxHdmiPort.SelectedIndex); StephaneLenclud@168: Properties.Settings.Default.CecHdmiPort++; StephaneLenclud@168: Properties.Settings.Default.Save(); StephaneLenclud@168: // StephaneLenclud@168: ResetCec(); StephaneLenclud@168: } StephaneLenclud@168: StephaneLenclud@168: private void checkBoxCecMonitorOff_CheckedChanged(object sender, EventArgs e) StephaneLenclud@168: { StephaneLenclud@168: Properties.Settings.Default.CecMonitorOff = checkBoxCecMonitorOff.Checked; StephaneLenclud@168: Properties.Settings.Default.Save(); StephaneLenclud@168: // StephaneLenclud@168: ResetCec(); StephaneLenclud@168: } StephaneLenclud@168: StephaneLenclud@168: private void checkBoxCecMonitorOn_CheckedChanged(object sender, EventArgs e) StephaneLenclud@168: { StephaneLenclud@168: Properties.Settings.Default.CecMonitorOn = checkBoxCecMonitorOn.Checked; StephaneLenclud@168: Properties.Settings.Default.Save(); StephaneLenclud@168: // StephaneLenclud@168: ResetCec(); StephaneLenclud@168: } StephaneLenclud@168: StephaneLenclud@204: private void checkBoxCecReconnectToPowerTv_CheckedChanged(object sender, EventArgs e) StephaneLenclud@204: { StephaneLenclud@204: Properties.Settings.Default.CecReconnectToPowerTv = checkBoxCecReconnectToPowerTv.Checked; StephaneLenclud@204: Properties.Settings.Default.Save(); StephaneLenclud@204: // StephaneLenclud@204: ResetCec(); StephaneLenclud@204: } StephaneLenclud@204: StephaneLenclud@168: /// StephaneLenclud@168: /// StephaneLenclud@168: /// StephaneLenclud@168: private void ResetCec() StephaneLenclud@168: { StephaneLenclud@168: if (iCecManager==null) StephaneLenclud@168: { StephaneLenclud@168: //Thus skipping initial UI setup StephaneLenclud@168: return; StephaneLenclud@168: } StephaneLenclud@168: StephaneLenclud@168: iCecManager.Stop(); StephaneLenclud@168: // StephaneLenclud@168: if (Properties.Settings.Default.CecEnabled) StephaneLenclud@168: { StephaneLenclud@168: iCecManager.Start(Handle, "CEC", StephaneLenclud@168: Properties.Settings.Default.CecHdmiPort, StephaneLenclud@168: Properties.Settings.Default.CecMonitorOn, StephaneLenclud@204: Properties.Settings.Default.CecMonitorOff, StephaneLenclud@204: Properties.Settings.Default.CecReconnectToPowerTv); StephaneLenclud@206: Stephane@207: SetupCecLogLevel(); Stephane@207: } Stephane@207: } StephaneLenclud@206: Stephane@207: /// Stephane@207: /// Stephane@207: /// Stephane@207: private void SetupCecLogLevel() Stephane@207: { Stephane@207: //Setup log level Stephane@207: iCecManager.Client.LogLevel = 0; StephaneLenclud@206: Stephane@207: if (checkBoxCecLogError.Checked) Stephane@207: iCecManager.Client.LogLevel |= (int)CecLogLevel.Error; StephaneLenclud@206: Stephane@207: if (checkBoxCecLogWarning.Checked) Stephane@207: iCecManager.Client.LogLevel |= (int)CecLogLevel.Warning; StephaneLenclud@206: Stephane@207: if (checkBoxCecLogNotice.Checked) Stephane@207: iCecManager.Client.LogLevel |= (int)CecLogLevel.Notice; StephaneLenclud@206: Stephane@207: if (checkBoxCecLogTraffic.Checked) Stephane@207: iCecManager.Client.LogLevel |= (int)CecLogLevel.Traffic; StephaneLenclud@206: Stephane@207: if (checkBoxCecLogDebug.Checked) Stephane@207: iCecManager.Client.LogLevel |= (int)CecLogLevel.Debug; StephaneLenclud@206: Stephane@207: iCecManager.Client.FilterOutPollLogs = checkBoxCecLogNoPoll.Checked; Stephane@207: StephaneLenclud@168: } StephaneLenclud@189: StephaneLenclud@189: private void ButtonStartIdleClient_Click(object sender, EventArgs e) StephaneLenclud@189: { StephaneLenclud@189: StartIdleClient(); StephaneLenclud@189: } StephaneLenclud@204: StephaneLenclud@206: private void buttonClearLogs_Click(object sender, EventArgs e) StephaneLenclud@206: { StephaneLenclud@206: richTextBoxLogs.Clear(); StephaneLenclud@206: } StephaneLenclud@204: StephaneLenclud@206: private void checkBoxCecLogs_CheckedChanged(object sender, EventArgs e) StephaneLenclud@206: { Stephane@207: SetupCecLogLevel(); StephaneLenclud@206: } StephaneLenclud@211: StephaneLenclud@211: private void buttonAddAction_Click(object sender, EventArgs e) StephaneLenclud@211: { Stephane@212: if (iTreeViewEvents.SelectedNode==null) Stephane@212: { Stephane@212: return; Stephane@212: } Stephane@212: Stephane@212: Event earEvent = (Event)iTreeViewEvents.SelectedNode.Tag; Stephane@212: if (earEvent == null) StephaneLenclud@211: { StephaneLenclud@211: //Must select event node StephaneLenclud@211: return; StephaneLenclud@211: } StephaneLenclud@211: StephaneLenclud@211: FormEditAction ea = new FormEditAction(); StephaneLenclud@211: DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea); StephaneLenclud@211: if (res == DialogResult.OK) StephaneLenclud@211: { Stephane@212: earEvent.Actions.Add(ea.Action); Stephane@212: Properties.Settings.Default.Actions = ManagerEventAction.Current; Stephane@212: Properties.Settings.Default.Save(); StephaneLenclud@211: SetupEvents(); StephaneLenclud@211: } StephaneLenclud@211: } sl@0: } sl@0: }