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: using System.Runtime.InteropServices; StephaneLenclud@268: using System.Security; StephaneLenclud@272: //CSCore StephaneLenclud@273: using CSCore; StephaneLenclud@273: using CSCore.Win32; StephaneLenclud@273: using CSCore.DSP; StephaneLenclud@273: using CSCore.Streams; StephaneLenclud@272: using CSCore.CoreAudioAPI; StephaneLenclud@273: using CSCore.SoundIn; StephaneLenclud@273: // Visualization StephaneLenclud@273: using Visualization; StephaneLenclud@272: // CEC 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@234: using Ear = SharpLib.Ear; StephaneLenclud@273: StephaneLenclud@124: sl@0: namespace SharpDisplayManager sl@0: { sl@58: //Types declarations sl@58: public delegate uint ColorProcessingDelegate(int aX, int aY, uint aPixel); StephaneLenclud@223: sl@58: public delegate int CoordinateTranslationDelegate(System.Drawing.Bitmap aBmp, int aInt); StephaneLenclud@223: sl@62: //Delegates are used for our thread safe method sl@62: public delegate void AddClientDelegate(string aSessionId, ICallback aCallback); StephaneLenclud@223: sl@62: public delegate void RemoveClientDelegate(string aSessionId); StephaneLenclud@223: sl@79: public delegate void SetFieldDelegate(string SessionId, DataField aField); StephaneLenclud@223: sl@79: public delegate void SetFieldsDelegate(string SessionId, System.Collections.Generic.IList aFields); StephaneLenclud@223: sl@62: public delegate void SetLayoutDelegate(string SessionId, TableLayout aLayout); StephaneLenclud@223: sl@62: public delegate void SetClientNameDelegate(string aSessionId, string aName); StephaneLenclud@223: StephaneLenclud@184: public delegate void SetClientPriorityDelegate(string aSessionId, uint aPriority); StephaneLenclud@223: StephaneLenclud@184: public delegate void PlainUpdateDelegate(); StephaneLenclud@223: StephaneLenclud@167: public delegate void WndProcDelegate(ref Message aMessage); sl@58: sl@58: /// sl@58: /// Our Display manager main form sl@58: /// StephaneLenclud@223: [System.ComponentModel.DesignerCategory("Form")] StephaneLenclud@226: public partial class FormMain : FormMainHid, IMMNotificationClient sl@0: { StephaneLenclud@234: //public Manager iManager = new Manager(); 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@223: // StephaneLenclud@223: 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@272: //CSCore StephaneLenclud@273: // Volume management StephaneLenclud@223: private MMDeviceEnumerator iMultiMediaDeviceEnumerator; StephaneLenclud@223: private MMDevice iMultiMediaDevice; StephaneLenclud@272: private AudioEndpointVolume iAudioEndpointVolume; StephaneLenclud@273: // Audio visualization StephaneLenclud@273: private WasapiCapture iSoundIn; StephaneLenclud@273: private IWaveSource iWaveSource; StephaneLenclud@273: private LineSpectrum iLineSpectrum; StephaneLenclud@273: StephaneLenclud@223: //Network StephaneLenclud@223: 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@223: StephaneLenclud@223: /// StephaneLenclud@223: /// Manage run when Windows startup option StephaneLenclud@223: /// StephaneLenclud@223: private StartupManager iStartupManager; StephaneLenclud@223: StephaneLenclud@223: /// StephaneLenclud@223: /// System notification icon used to hide our application from the task bar. StephaneLenclud@223: /// StephaneLenclud@223: 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: /// StephaneLenclud@253: RichTextBoxTraceListener 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: StephaneLenclud@226: public FormMain() sl@0: { StephaneLenclud@235: if (Properties.Settings.Default.EarManager == null) Stephane@212: { Stephane@212: //No actions in our settings yet StephaneLenclud@235: Properties.Settings.Default.EarManager = new EarManager(); Stephane@212: } Stephane@212: else Stephane@212: { StephaneLenclud@235: // We loaded events and actions from our settings StephaneLenclud@235: // Internalizer apparently skips constructor so we need to initialize it here StephaneLenclud@235: // Though I reckon that should only be needed when loading an empty EAR manager I guess. Stephane@243: Properties.Settings.Default.EarManager.Construct(); Stephane@212: } StephaneLenclud@210: iSkipFrameRendering = false; StephaneLenclud@223: iClosing = false; sl@65: iCurrentClientSessionId = ""; sl@65: iCurrentClientData = null; sl@2: LastTickTime = DateTime.Now; StephaneLenclud@223: //Instantiate our display and register for events notifications sl@3: iDisplay = new Display(); StephaneLenclud@223: iDisplay.OnOpened += OnDisplayOpened; StephaneLenclud@223: iDisplay.OnClosed += OnDisplayClosed; StephaneLenclud@223: // StephaneLenclud@223: iClients = new Dictionary(); StephaneLenclud@223: iStartupManager = new StartupManager(); StephaneLenclud@223: 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 StephaneLenclud@253: iWriter = new RichTextBoxTraceListener(richTextBoxLogs); StephaneLenclud@253: Trace.Listeners.Add(iWriter); StephaneLenclud@201: StephaneLenclud@201: //Populate device types StephaneLenclud@201: PopulateDeviceTypes(); StephaneLenclud@104: StephaneLenclud@223: //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@223: iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height, StephaneLenclud@223: PixelFormat.Format32bppArgb); sl@14: iCreateBitmap = false; sl@94: StephaneLenclud@223: //Minimize our window if desired StephaneLenclud@223: if (Properties.Settings.Default.StartMinimized) StephaneLenclud@223: { StephaneLenclud@223: WindowState = FormWindowState.Minimized; StephaneLenclud@223: } sl@94: sl@0: } sl@0: StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@106: private void MainForm_Load(object sender, EventArgs e) StephaneLenclud@106: { StephaneLenclud@223: //Check if we are running a Click Once deployed application StephaneLenclud@223: if (ApplicationDeployment.IsNetworkDeployed) StephaneLenclud@223: { StephaneLenclud@223: //This is a proper Click Once installation, fetch and show our version number StephaneLenclud@223: this.Text += " - v" + ApplicationDeployment.CurrentDeployment.CurrentVersion; StephaneLenclud@223: } StephaneLenclud@223: else StephaneLenclud@223: { StephaneLenclud@223: //Not a proper Click Once installation, assuming development build then StephaneLenclud@223: this.Text += " - development"; StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@272: //CSCore StephaneLenclud@223: iMultiMediaDeviceEnumerator = new MMDeviceEnumerator(); StephaneLenclud@223: iMultiMediaDeviceEnumerator.RegisterEndpointNotificationCallback(this); StephaneLenclud@223: UpdateAudioDeviceAndMasterVolumeThreadSafe(); StephaneLenclud@223: StephaneLenclud@223: //Network StephaneLenclud@223: iNetworkManager = new NetworkManager(); StephaneLenclud@223: iNetworkManager.OnConnectivityChanged += OnConnectivityChanged; StephaneLenclud@223: UpdateNetworkStatus(); StephaneLenclud@117: StephaneLenclud@167: //CEC StephaneLenclud@167: iCecManager = new ConsumerElectronicControl(); StephaneLenclud@167: OnWndProc += iCecManager.OnWndProc; StephaneLenclud@168: ResetCec(); StephaneLenclud@168: StephaneLenclud@233: //Harmony StephaneLenclud@255: ResetHarmonyAsync(); StephaneLenclud@233: StephaneLenclud@211: //Setup Events StephaneLenclud@260: PopulateTreeViewEvents(); 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@223: { StephaneLenclud@223: Visible = false; StephaneLenclud@223: } StephaneLenclud@106: StephaneLenclud@106: #if !DEBUG StephaneLenclud@223: //When not debugging we want the screen to be empty until a client takes over StephaneLenclud@106: ClearLayout(); StephaneLenclud@106: #else StephaneLenclud@223: //When developing we want at least one client for testing StephaneLenclud@223: StartNewClient("abcdefghijklmnopqrst-0123456789", "ABCDEFGHIJKLMNOPQRST-0123456789"); StephaneLenclud@106: #endif StephaneLenclud@106: StephaneLenclud@223: //Open display connection on start-up if needed StephaneLenclud@223: if (Properties.Settings.Default.DisplayConnectOnStartup) StephaneLenclud@223: { StephaneLenclud@223: OpenDisplayConnection(); StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: //Start our server so that we can get client requests StephaneLenclud@223: StartServer(); StephaneLenclud@223: StephaneLenclud@223: //Register for HID events StephaneLenclud@223: 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@223: /// StephaneLenclud@223: /// Called when our display is opened. StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: private void OnDisplayOpened(Display aDisplay) StephaneLenclud@223: { StephaneLenclud@187: //Make sure we resume frame rendering StephaneLenclud@187: iSkipFrameRendering = false; StephaneLenclud@122: StephaneLenclud@223: //Set our screen size now that our display is connected StephaneLenclud@223: //Our panelDisplay is the container of our tableLayoutPanel StephaneLenclud@223: //tableLayoutPanel will resize itself to fit the client size of our panelDisplay StephaneLenclud@223: //panelDisplay needs an extra 2 pixels for borders on each sides StephaneLenclud@223: //tableLayoutPanel will eventually be the exact size of our display StephaneLenclud@223: Size size = new Size(iDisplay.WidthInPixels() + 2, iDisplay.HeightInPixels() + 2); StephaneLenclud@223: panelDisplay.Size = size; StephaneLenclud@223: StephaneLenclud@223: //Our display was just opened, update our UI StephaneLenclud@223: UpdateStatus(); StephaneLenclud@223: //Initiate asynchronous request StephaneLenclud@223: iDisplay.RequestFirmwareRevision(); StephaneLenclud@223: StephaneLenclud@223: //Audio StephaneLenclud@223: UpdateMasterVolumeThreadSafe(); StephaneLenclud@223: //Network StephaneLenclud@223: UpdateNetworkStatus(); StephaneLenclud@112: StephaneLenclud@108: #if DEBUG StephaneLenclud@223: //Testing icon in debug, no arm done if icon not supported StephaneLenclud@223: //iDisplay.SetIconStatus(Display.TMiniDisplayIconType.EMiniDisplayIconRecording, 0, 1); StephaneLenclud@223: //iDisplay.SetAllIconsStatus(2); StephaneLenclud@108: #endif StephaneLenclud@108: StephaneLenclud@223: } StephaneLenclud@104: StephaneLenclud@265: StephaneLenclud@265: private static void AddActionsToTreeNode(TreeNode aParentNode, Ear.Object aObject) StephaneLenclud@265: { StephaneLenclud@265: foreach (Ear.Action a in aObject.Objects.OfType()) StephaneLenclud@265: { StephaneLenclud@265: //Create action node StephaneLenclud@265: TreeNode actionNode = aParentNode.Nodes.Add(a.Brief()); StephaneLenclud@265: actionNode.Tag = a; StephaneLenclud@265: //Use color from parent unless our action itself is disabled StephaneLenclud@265: actionNode.ForeColor = a.Enabled ? aParentNode.ForeColor : Color.DimGray; StephaneLenclud@265: //Go recursive StephaneLenclud@265: AddActionsToTreeNode(actionNode,a); StephaneLenclud@265: } StephaneLenclud@265: } StephaneLenclud@265: StephaneLenclud@265: StephaneLenclud@265: /// StephaneLenclud@265: /// StephaneLenclud@265: /// StephaneLenclud@265: /// StephaneLenclud@265: /// StephaneLenclud@265: private static TreeNode FindTreeNodeForEarObject(Ear.Object aObject, TreeNode aNode) StephaneLenclud@265: { StephaneLenclud@265: if (aNode.Tag == aObject) StephaneLenclud@265: { StephaneLenclud@265: return aNode; StephaneLenclud@265: } StephaneLenclud@265: StephaneLenclud@265: foreach (TreeNode n in aNode.Nodes) StephaneLenclud@265: { StephaneLenclud@265: TreeNode found = FindTreeNodeForEarObject(aObject,n); StephaneLenclud@265: if (found != null) StephaneLenclud@265: { StephaneLenclud@265: return found; StephaneLenclud@265: } StephaneLenclud@265: } StephaneLenclud@265: StephaneLenclud@265: return null; StephaneLenclud@265: } StephaneLenclud@265: StephaneLenclud@265: StephaneLenclud@265: /// StephaneLenclud@265: /// StephaneLenclud@265: /// StephaneLenclud@265: /// StephaneLenclud@265: private void SelectEarObject(Ear.Object aObject) StephaneLenclud@265: { StephaneLenclud@265: foreach (TreeNode n in iTreeViewEvents.Nodes) StephaneLenclud@265: { StephaneLenclud@265: TreeNode found = FindTreeNodeForEarObject(aObject, n); StephaneLenclud@265: if (found != null) StephaneLenclud@265: { StephaneLenclud@265: iTreeViewEvents.SelectedNode=found; StephaneLenclud@265: iTreeViewEvents.Focus(); StephaneLenclud@265: return; StephaneLenclud@265: } StephaneLenclud@265: } StephaneLenclud@265: } StephaneLenclud@265: StephaneLenclud@211: /// StephaneLenclud@218: /// Populate tree view with events and actions StephaneLenclud@211: /// StephaneLenclud@266: private void PopulateTreeViewEvents(Ear.Object aSelectedObject=null) StephaneLenclud@211: { StephaneLenclud@211: //Reset our tree StephaneLenclud@211: iTreeViewEvents.Nodes.Clear(); StephaneLenclud@211: //Populate registered events StephaneLenclud@235: foreach (Ear.Event e in Properties.Settings.Default.EarManager.Events) StephaneLenclud@211: { StephaneLenclud@231: //Create our event node StephaneLenclud@260: //Work out the name of our node StephaneLenclud@260: string eventNodeName = ""; StephaneLenclud@260: if (!string.IsNullOrEmpty(e.Name)) StephaneLenclud@260: { StephaneLenclud@260: //That event has a proper name, use it then StephaneLenclud@260: eventNodeName = $"{e.Name} - {e.Brief()}"; StephaneLenclud@260: } StephaneLenclud@260: else StephaneLenclud@260: { StephaneLenclud@260: //Unnamed events just use brief StephaneLenclud@260: eventNodeName = e.Brief(); StephaneLenclud@260: } StephaneLenclud@260: StephaneLenclud@260: TreeNode eventNode = iTreeViewEvents.Nodes.Add(eventNodeName); StephaneLenclud@231: eventNode.Tag = e; //For easy access to our event StephaneLenclud@231: if (!e.Enabled) StephaneLenclud@231: { StephaneLenclud@231: //Dim our nodes if disabled StephaneLenclud@231: eventNode.ForeColor = Color.DimGray; StephaneLenclud@231: } StephaneLenclud@231: StephaneLenclud@231: //Add event description as child node StephaneLenclud@260: eventNode.Nodes.Add(e.AttributeDescription).ForeColor = eventNode.ForeColor; StephaneLenclud@231: //Create child node for actions root StephaneLenclud@265: TreeNode actionsNode = eventNode.Nodes.Add("Actions"); StephaneLenclud@265: actionsNode.ForeColor = eventNode.ForeColor; StephaneLenclud@265: StephaneLenclud@265: // Recursively add our actions for that event StephaneLenclud@265: AddActionsToTreeNode(actionsNode,e); StephaneLenclud@211: } StephaneLenclud@211: StephaneLenclud@214: iTreeViewEvents.ExpandAll(); StephaneLenclud@231: StephaneLenclud@266: if (aSelectedObject != null) StephaneLenclud@266: { StephaneLenclud@266: SelectEarObject(aSelectedObject); StephaneLenclud@266: } StephaneLenclud@266: StephaneLenclud@266: // Just to be safe in case the selection did not work StephaneLenclud@231: UpdateEventView(); StephaneLenclud@211: } StephaneLenclud@211: StephaneLenclud@223: /// StephaneLenclud@223: /// Called when our display is closed. StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: private void OnDisplayClosed(Display aDisplay) StephaneLenclud@223: { StephaneLenclud@187: //Our display was just closed, update our UI consequently StephaneLenclud@187: UpdateStatus(); StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: public void OnConnectivityChanged(NetworkManager aNetwork, NLM_CONNECTIVITY newConnectivity) StephaneLenclud@223: { StephaneLenclud@223: //Update network status StephaneLenclud@223: UpdateNetworkStatus(); StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: /// StephaneLenclud@223: /// Update our Network Status StephaneLenclud@223: /// StephaneLenclud@223: private void UpdateNetworkStatus() StephaneLenclud@223: { StephaneLenclud@223: if (iDisplay.IsOpen()) StephaneLenclud@223: { StephaneLenclud@223: iDisplay.SetIconOnOff(MiniDisplay.IconType.Internet, StephaneLenclud@223: iNetworkManager.NetworkListManager.IsConnectedToInternet); StephaneLenclud@135: iDisplay.SetIconOnOff(MiniDisplay.IconType.NetworkSignal, iNetworkManager.NetworkListManager.IsConnected); StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: StephaneLenclud@223: int iLastNetworkIconIndex = 0; StephaneLenclud@223: int iUpdateCountSinceLastNetworkAnimation = 0; StephaneLenclud@223: StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: private void UpdateNetworkSignal(DateTime aLastTickTime, DateTime aNewTickTime) StephaneLenclud@223: { StephaneLenclud@223: iUpdateCountSinceLastNetworkAnimation++; StephaneLenclud@223: iUpdateCountSinceLastNetworkAnimation = iUpdateCountSinceLastNetworkAnimation%4; StephaneLenclud@223: StephaneLenclud@223: if (iDisplay.IsOpen() && iNetworkManager.NetworkListManager.IsConnected && StephaneLenclud@223: iUpdateCountSinceLastNetworkAnimation == 0) StephaneLenclud@223: { StephaneLenclud@135: int iconCount = iDisplay.IconCount(MiniDisplay.IconType.NetworkSignal); StephaneLenclud@223: if (iconCount <= 0) StephaneLenclud@223: { StephaneLenclud@223: //Prevents div by zero and other undefined behavior StephaneLenclud@223: return; StephaneLenclud@223: } StephaneLenclud@223: iLastNetworkIconIndex++; StephaneLenclud@223: iLastNetworkIconIndex = iLastNetworkIconIndex%(iconCount*2); StephaneLenclud@223: for (int i = 0; i < iconCount; i++) StephaneLenclud@223: { StephaneLenclud@223: if (i < iLastNetworkIconIndex && !(i == 0 && iLastNetworkIconIndex > 3) && StephaneLenclud@223: !(i == 1 && iLastNetworkIconIndex > 4)) StephaneLenclud@223: { StephaneLenclud@135: iDisplay.SetIconOn(MiniDisplay.IconType.NetworkSignal, i); StephaneLenclud@223: } StephaneLenclud@223: else StephaneLenclud@223: { StephaneLenclud@135: iDisplay.SetIconOff(MiniDisplay.IconType.NetworkSignal, i); StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: } 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@272: public void OnVolumeNotificationThreadSafe(object sender, AudioEndpointVolumeCallbackEventArgs aEvent) StephaneLenclud@112: { StephaneLenclud@223: 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@223: //Just like Windows Volume Mixer we unmute if the volume is adjusted StephaneLenclud@272: iAudioEndpointVolume.IsMuted = false; StephaneLenclud@223: //Set volume level according to our volume slider new position StephaneLenclud@272: iAudioEndpointVolume.MasterVolumeLevelScalar = trackBarMasterVolume.Value/100.0f; StephaneLenclud@112: } StephaneLenclud@112: StephaneLenclud@113: StephaneLenclud@223: /// StephaneLenclud@223: /// Mute check box changed. StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: private void checkBoxMute_CheckedChanged(object sender, EventArgs e) StephaneLenclud@223: { StephaneLenclud@272: iAudioEndpointVolume.IsMuted = checkBoxMute.Checked; StephaneLenclud@223: } StephaneLenclud@113: StephaneLenclud@112: /// StephaneLenclud@112: /// Device State Changed StephaneLenclud@112: /// StephaneLenclud@223: public void OnDeviceStateChanged([MarshalAs(UnmanagedType.LPWStr)] string deviceId, StephaneLenclud@223: [MarshalAs(UnmanagedType.I4)] DeviceState newState) StephaneLenclud@223: { StephaneLenclud@223: } StephaneLenclud@112: StephaneLenclud@112: /// StephaneLenclud@112: /// Device Added StephaneLenclud@112: /// StephaneLenclud@223: public void OnDeviceAdded([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId) StephaneLenclud@223: { StephaneLenclud@223: } StephaneLenclud@112: StephaneLenclud@112: /// StephaneLenclud@112: /// Device Removed StephaneLenclud@112: /// StephaneLenclud@223: public void OnDeviceRemoved([MarshalAs(UnmanagedType.LPWStr)] string deviceId) StephaneLenclud@223: { StephaneLenclud@223: } StephaneLenclud@112: StephaneLenclud@112: /// StephaneLenclud@112: /// Default Device Changed StephaneLenclud@112: /// StephaneLenclud@223: public void OnDefaultDeviceChanged(DataFlow flow, Role role, StephaneLenclud@223: [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@223: public void OnPropertyValueChanged([MarshalAs(UnmanagedType.LPWStr)] string pwstrDeviceId, PropertyKey key) StephaneLenclud@223: { StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: StephaneLenclud@223: StephaneLenclud@223: StephaneLenclud@223: /// StephaneLenclud@223: /// Update master volume indicators based our current system states. StephaneLenclud@223: /// This typically includes volume levels and mute status. StephaneLenclud@223: /// StephaneLenclud@223: private void UpdateMasterVolumeThreadSafe() StephaneLenclud@223: { StephaneLenclud@223: if (this.InvokeRequired) StephaneLenclud@223: { StephaneLenclud@223: //Not in the proper thread, invoke ourselves StephaneLenclud@223: PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateMasterVolumeThreadSafe); StephaneLenclud@223: this.Invoke(d, new object[] {}); StephaneLenclud@223: return; StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: //Update volume slider StephaneLenclud@272: float volumeLevelScalar = iAudioEndpointVolume.MasterVolumeLevelScalar; StephaneLenclud@223: trackBarMasterVolume.Value = Convert.ToInt32(volumeLevelScalar*100); StephaneLenclud@223: //Update mute checkbox StephaneLenclud@272: checkBoxMute.Checked = iAudioEndpointVolume.IsMuted; StephaneLenclud@223: StephaneLenclud@223: //If our display connection is open we need to update its icons StephaneLenclud@223: if (iDisplay.IsOpen()) StephaneLenclud@223: { StephaneLenclud@223: //First take care our our volume level icons StephaneLenclud@135: int volumeIconCount = iDisplay.IconCount(MiniDisplay.IconType.Volume); StephaneLenclud@223: if (volumeIconCount > 0) StephaneLenclud@223: { StephaneLenclud@223: //Compute current volume level from system level and the number of segments in our display volume bar. StephaneLenclud@223: //That tells us how many segments in our volume bar needs to be turned on. StephaneLenclud@223: float currentVolume = volumeLevelScalar*volumeIconCount; StephaneLenclud@223: int segmentOnCount = Convert.ToInt32(currentVolume); StephaneLenclud@223: //Check if our segment count was rounded up, this will later be used for half brightness segment StephaneLenclud@223: bool roundedUp = segmentOnCount > currentVolume; StephaneLenclud@223: StephaneLenclud@223: for (int i = 0; i < volumeIconCount; i++) StephaneLenclud@223: { StephaneLenclud@223: if (i < segmentOnCount) StephaneLenclud@223: { StephaneLenclud@223: //If we are dealing with our last segment and our segment count was rounded up then we will use half brightness. StephaneLenclud@223: if (i == segmentOnCount - 1 && roundedUp) StephaneLenclud@223: { StephaneLenclud@223: //Half brightness StephaneLenclud@223: iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, StephaneLenclud@223: (iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1)/2); StephaneLenclud@223: } StephaneLenclud@223: else StephaneLenclud@223: { StephaneLenclud@223: //Full brightness StephaneLenclud@223: iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, StephaneLenclud@223: iDisplay.IconStatusCount(MiniDisplay.IconType.Volume) - 1); StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: else StephaneLenclud@223: { StephaneLenclud@135: iDisplay.SetIconStatus(MiniDisplay.IconType.Volume, i, 0); StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: //Take care of our mute icon StephaneLenclud@272: iDisplay.SetIconOnOff(MiniDisplay.IconType.Mute, iAudioEndpointVolume.IsMuted); StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: } StephaneLenclud@112: StephaneLenclud@112: /// StephaneLenclud@112: /// StephaneLenclud@112: /// StephaneLenclud@273: private void StartAudioVisualization() StephaneLenclud@273: { StephaneLenclud@273: StopAudioVisualization(); StephaneLenclud@273: //Open the default device StephaneLenclud@273: iSoundIn = new WasapiLoopbackCapture(); StephaneLenclud@273: //Our loopback capture opens the default render device by default so the following is not needed StephaneLenclud@273: //iSoundIn.Device = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Console); StephaneLenclud@273: iSoundIn.Initialize(); StephaneLenclud@273: StephaneLenclud@273: SoundInSource soundInSource = new SoundInSource(iSoundIn); StephaneLenclud@273: ISampleSource source = soundInSource.ToSampleSource(); StephaneLenclud@273: StephaneLenclud@273: const FftSize fftSize = FftSize.Fft4096; StephaneLenclud@273: //create a spectrum provider which provides fft data based on some input StephaneLenclud@273: BasicSpectrumProvider spectrumProvider = new BasicSpectrumProvider(source.WaveFormat.Channels, source.WaveFormat.SampleRate, fftSize); StephaneLenclud@273: StephaneLenclud@273: //linespectrum and voiceprint3dspectrum used for rendering some fft data StephaneLenclud@273: //in oder to get some fft data, set the previously created spectrumprovider StephaneLenclud@273: iLineSpectrum = new LineSpectrum(fftSize) StephaneLenclud@273: { StephaneLenclud@273: SpectrumProvider = spectrumProvider, StephaneLenclud@274: UseAverage = false, StephaneLenclud@274: BarCount = 16, StephaneLenclud@274: BarSpacing = 1, StephaneLenclud@273: IsXLogScale = true, StephaneLenclud@274: ScalingStrategy = ScalingStrategy.Decibel StephaneLenclud@273: }; StephaneLenclud@273: StephaneLenclud@273: StephaneLenclud@273: //the SingleBlockNotificationStream is used to intercept the played samples StephaneLenclud@273: var notificationSource = new SingleBlockNotificationStream(source); StephaneLenclud@273: //pass the intercepted samples as input data to the spectrumprovider (which will calculate a fft based on them) StephaneLenclud@273: notificationSource.SingleBlockRead += (s, a) => spectrumProvider.Add(a.Left, a.Right); StephaneLenclud@273: StephaneLenclud@273: iWaveSource = notificationSource.ToWaveSource(16); StephaneLenclud@273: StephaneLenclud@273: StephaneLenclud@273: // We need to read from our source otherwise SingleBlockRead is never called and our spectrum provider is not populated StephaneLenclud@273: byte[] buffer = new byte[iWaveSource.WaveFormat.BytesPerSecond / 2]; StephaneLenclud@273: soundInSource.DataAvailable += (s, aEvent) => StephaneLenclud@273: { StephaneLenclud@273: int read; StephaneLenclud@273: while ((read = iWaveSource.Read(buffer, 0, buffer.Length)) > 0) ; StephaneLenclud@273: }; StephaneLenclud@273: StephaneLenclud@273: StephaneLenclud@273: //Start recording StephaneLenclud@273: iSoundIn.Start(); StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: /// StephaneLenclud@273: /// StephaneLenclud@273: /// StephaneLenclud@273: private void StopAudioVisualization() StephaneLenclud@273: { StephaneLenclud@273: StephaneLenclud@273: if (iSoundIn != null) StephaneLenclud@273: { StephaneLenclud@273: iSoundIn.Stop(); StephaneLenclud@273: iSoundIn.Dispose(); StephaneLenclud@273: iSoundIn = null; StephaneLenclud@273: } StephaneLenclud@273: if (iWaveSource != null) StephaneLenclud@273: { StephaneLenclud@273: iWaveSource.Dispose(); StephaneLenclud@273: iWaveSource = null; StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: StephaneLenclud@273: /// StephaneLenclud@273: /// StephaneLenclud@273: /// StephaneLenclud@274: private void UpdateAudioVisualization() StephaneLenclud@273: { StephaneLenclud@273: // For demo draft purposes just fetch the firt picture box control and update it with current audio spectrum StephaneLenclud@274: StephaneLenclud@274: if (iCurrentClientData == null) StephaneLenclud@273: { StephaneLenclud@274: return; StephaneLenclud@274: } StephaneLenclud@274: StephaneLenclud@274: // Update our math StephaneLenclud@274: if (!iLineSpectrum.Update()) StephaneLenclud@274: { StephaneLenclud@274: //Nothing changed no need to render StephaneLenclud@274: return; StephaneLenclud@274: } StephaneLenclud@274: StephaneLenclud@274: // Check if our current client has an Audio Visualizer field StephaneLenclud@274: // and render them as needed StephaneLenclud@274: foreach (DataField f in iCurrentClientData.Fields) StephaneLenclud@274: { StephaneLenclud@274: if (f is AudioVisualizerField) StephaneLenclud@273: { StephaneLenclud@274: AudioVisualizerField avf = (AudioVisualizerField)f; StephaneLenclud@274: Control ctrl = iTableLayoutPanel.GetControlFromPosition(avf.Column, avf.Row); StephaneLenclud@274: StephaneLenclud@274: if (ctrl is PictureBox) StephaneLenclud@273: { StephaneLenclud@274: PictureBox pb = (PictureBox)ctrl; StephaneLenclud@274: Image image = pb.Image; StephaneLenclud@274: // TODO: recycle images StephaneLenclud@274: var newImage = iLineSpectrum.Render(pb.Size, Color.Black, Color.Black, Color.White, false); StephaneLenclud@274: if (newImage != null) StephaneLenclud@274: { StephaneLenclud@274: pb.Image = newImage; StephaneLenclud@274: if (image != null) StephaneLenclud@274: image.Dispose(); StephaneLenclud@274: } StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: } StephaneLenclud@273: StephaneLenclud@273: StephaneLenclud@273: /// StephaneLenclud@273: /// StephaneLenclud@273: /// StephaneLenclud@112: private void UpdateAudioDeviceAndMasterVolumeThreadSafe() StephaneLenclud@112: { StephaneLenclud@112: if (this.InvokeRequired) StephaneLenclud@112: { StephaneLenclud@112: //Not in the proper thread, invoke ourselves StephaneLenclud@223: PlainUpdateDelegate d = new PlainUpdateDelegate(UpdateAudioDeviceAndMasterVolumeThreadSafe); StephaneLenclud@223: this.Invoke(d, new object[] {}); StephaneLenclud@112: return; StephaneLenclud@112: } StephaneLenclud@223: StephaneLenclud@112: //We are in the correct thread just go ahead. StephaneLenclud@112: try StephaneLenclud@223: { StephaneLenclud@272: //Get our master volume StephaneLenclud@223: iMultiMediaDevice = iMultiMediaDeviceEnumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); StephaneLenclud@272: iAudioEndpointVolume = AudioEndpointVolume.FromDevice(iMultiMediaDevice); StephaneLenclud@273: StephaneLenclud@223: //Update our label StephaneLenclud@223: labelDefaultAudioDevice.Text = iMultiMediaDevice.FriendlyName; StephaneLenclud@116: StephaneLenclud@112: //Show our volume in our track bar StephaneLenclud@223: UpdateMasterVolumeThreadSafe(); StephaneLenclud@112: StephaneLenclud@112: //Register to get volume modifications StephaneLenclud@272: AudioEndpointVolumeCallback callback = new AudioEndpointVolumeCallback(); StephaneLenclud@272: callback.NotifyRecived += OnVolumeNotificationThreadSafe; StephaneLenclud@272: // Do we need to unregister? StephaneLenclud@273: iAudioEndpointVolume.RegisterControlChangeNotify(callback); StephaneLenclud@273: // StephaneLenclud@273: StartAudioVisualization(); StephaneLenclud@112: // StephaneLenclud@223: 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@223: trackBarMasterVolume.Enabled = false; StephaneLenclud@112: } StephaneLenclud@112: } StephaneLenclud@104: StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: private void PopulateDeviceTypes() StephaneLenclud@223: { StephaneLenclud@223: int count = Display.TypeCount(); StephaneLenclud@223: StephaneLenclud@223: for (int i = 0; i < count; i++) StephaneLenclud@223: { StephaneLenclud@223: comboBoxDisplayType.Items.Add(Display.TypeName((MiniDisplay.Type) i)); StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@104: StephaneLenclud@152: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: private void SetupTrayIcon() StephaneLenclud@223: { StephaneLenclud@223: iNotifyIcon.Icon = GetIcon("vfd.ico"); StephaneLenclud@223: iNotifyIcon.Text = "Sharp Display Manager"; StephaneLenclud@223: iNotifyIcon.Visible = true; StephaneLenclud@223: StephaneLenclud@223: //Double click toggles visibility - typically brings up the application StephaneLenclud@223: iNotifyIcon.DoubleClick += delegate(object obj, EventArgs args) StephaneLenclud@223: { StephaneLenclud@223: SysTrayHideShow(); StephaneLenclud@223: }; StephaneLenclud@223: StephaneLenclud@223: //Adding a context menu, useful to be able to exit the application StephaneLenclud@223: ContextMenu contextMenu = new ContextMenu(); StephaneLenclud@223: //Context menu item to toggle visibility StephaneLenclud@223: MenuItem hideShowItem = new MenuItem("Hide/Show"); StephaneLenclud@223: hideShowItem.Click += delegate(object obj, EventArgs args) StephaneLenclud@223: { StephaneLenclud@223: SysTrayHideShow(); StephaneLenclud@223: }; StephaneLenclud@223: contextMenu.MenuItems.Add(hideShowItem); StephaneLenclud@223: StephaneLenclud@223: //Context menu item separator StephaneLenclud@223: contextMenu.MenuItems.Add(new MenuItem("-")); StephaneLenclud@223: StephaneLenclud@223: //Context menu exit item StephaneLenclud@223: MenuItem exitItem = new MenuItem("Exit"); StephaneLenclud@223: exitItem.Click += delegate(object obj, EventArgs args) StephaneLenclud@223: { StephaneLenclud@223: Application.Exit(); StephaneLenclud@223: }; StephaneLenclud@223: contextMenu.MenuItems.Add(exitItem); StephaneLenclud@223: StephaneLenclud@223: iNotifyIcon.ContextMenu = contextMenu; StephaneLenclud@223: } 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) StephaneLenclud@223: { StephaneLenclud@223: string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames(); StephaneLenclud@223: foreach (string name in names) StephaneLenclud@223: { StephaneLenclud@178: //Find a resource name that ends with the given name StephaneLenclud@223: if (name.EndsWith(aName)) StephaneLenclud@223: { StephaneLenclud@223: using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name)) StephaneLenclud@223: { StephaneLenclud@223: return new Icon(stream); StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: return null; StephaneLenclud@223: } 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@223: 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@223: requestedClientData.Priority == iCurrentClientData.Priority && StephaneLenclud@223: //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: { StephaneLenclud@223: ((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: { StephaneLenclud@223: 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: { StephaneLenclud@223: labelWarning.Text = "WARNING: Selected font is too height by " + (label.Font.Height - label.Height) + StephaneLenclud@223: " 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@223: ((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: { StephaneLenclud@223: if ((aX%2 == 0) && (aY%2 == 0)) sl@58: { sl@58: return ~aPixel; sl@58: } StephaneLenclud@223: 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: StephaneLenclud@270: /// StephaneLenclud@270: /// This is our timer tick responsible to perform our render StephaneLenclud@270: /// TODO: Use a threading timer instead of a Windows form timer. StephaneLenclud@270: /// StephaneLenclud@270: /// StephaneLenclud@270: /// sl@2: private void timer_Tick(object sender, EventArgs e) sl@14: { sl@2: //Update our animations sl@2: DateTime NewTickTime = DateTime.Now; sl@2: StephaneLenclud@223: 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: { StephaneLenclud@223: ((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@223: //Check if frame rendering is needed StephaneLenclud@223: //Typically used when showing clock StephaneLenclud@223: if (!iSkipFrameRendering) StephaneLenclud@223: { StephaneLenclud@223: //Draw to bitmap StephaneLenclud@223: if (iCreateBitmap) StephaneLenclud@223: { StephaneLenclud@223: iBmp = new System.Drawing.Bitmap(iTableLayoutPanel.Width, iTableLayoutPanel.Height, StephaneLenclud@223: PixelFormat.Format32bppArgb); StephaneLenclud@158: iCreateBitmap = false; StephaneLenclud@158: } StephaneLenclud@223: iTableLayoutPanel.DrawToBitmap(iBmp, iTableLayoutPanel.ClientRectangle); StephaneLenclud@223: //iBmp.Save("D:\\capture.png"); StephaneLenclud@223: StephaneLenclud@223: //Send it to our display StephaneLenclud@223: for (int i = 0; i < iBmp.Width; i++) StephaneLenclud@223: { StephaneLenclud@223: for (int j = 0; j < iBmp.Height; j++) StephaneLenclud@223: { StephaneLenclud@223: unchecked StephaneLenclud@223: { StephaneLenclud@223: //Get our processed pixel coordinates StephaneLenclud@223: int x = iScreenX(iBmp, i); StephaneLenclud@223: int y = iScreenY(iBmp, j); StephaneLenclud@223: //Get pixel color StephaneLenclud@223: uint color = (uint) iBmp.GetPixel(i, j).ToArgb(); StephaneLenclud@223: //Apply color effects StephaneLenclud@223: color = iColorFx(x, y, color); StephaneLenclud@223: //Now set our pixel StephaneLenclud@223: iDisplay.SetPixel(x, y, color); StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: iDisplay.SwapBuffers(); StephaneLenclud@223: } sl@4: } sl@8: StephaneLenclud@274: UpdateAudioVisualization(); StephaneLenclud@273: sl@8: //Compute instant FPS StephaneLenclud@274: toolStripStatusLabelFps.Text = (1.0/NewTickTime.Subtract(LastTickTime).TotalSeconds).ToString("F0") + " / " + StephaneLenclud@270: (1000/iTimerDisplay.Interval).ToString() + " FPS"; sl@8: sl@8: LastTickTime = NewTickTime; sl@8: sl@2: } sl@3: StephaneLenclud@223: /// StephaneLenclud@223: /// Attempt to establish connection with our display hardware. StephaneLenclud@223: /// sl@46: private void OpenDisplayConnection() sl@3: { sl@46: CloseDisplayConnection(); sl@46: StephaneLenclud@223: if (!iDisplay.Open((MiniDisplay.Type) cds.DisplayType)) StephaneLenclud@223: { StephaneLenclud@223: UpdateStatus(); StephaneLenclud@223: toolStripStatusLabelConnect.Text = "Connection error"; sl@7: } sl@46: } sl@7: sl@46: private void CloseDisplayConnection() sl@46: { StephaneLenclud@223: //Status will be updated upon receiving the closed event StephaneLenclud@223: StephaneLenclud@223: if (iDisplay == null || !iDisplay.IsOpen()) StephaneLenclud@223: { StephaneLenclud@223: return; StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: //Do not clear if we gave up on rendering already. StephaneLenclud@223: //This means we will keep on displaying clock on MDM166AA for instance. StephaneLenclud@223: if (!iSkipFrameRendering) StephaneLenclud@223: { StephaneLenclud@223: iDisplay.Clear(); StephaneLenclud@223: iDisplay.SwapBuffers(); StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: 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(); StephaneLenclud@223: 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) StephaneLenclud@223: if (g.MeasureString(c.ToString(), ft, Int32.MaxValue, StringFormat.GenericTypographic).Width != StephaneLenclud@223: 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@223: /// StephaneLenclud@223: /// Synchronize UI with settings StephaneLenclud@223: /// sl@7: private void UpdateStatus() StephaneLenclud@223: { sl@48: //Load settings sl@54: checkBoxShowBorders.Checked = cds.ShowBorders; StephaneLenclud@223: iTableLayoutPanel.CellBorderStyle = (cds.ShowBorders StephaneLenclud@223: ? TableLayoutPanelCellBorderStyle.Single StephaneLenclud@223: : 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(); StephaneLenclud@223: //Check if "run on Windows startup" is enabled StephaneLenclud@223: checkBoxAutoStart.Checked = iStartupManager.Startup; StephaneLenclud@268: StephaneLenclud@168: //CEC settings 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@223: 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@223: maskedTextBoxScrollingSpeed.Text = cds.ScrollingSpeedInPixelsPerSecond.ToString(); sl@48: comboBoxDisplayType.SelectedIndex = cds.DisplayType; StephaneLenclud@270: iTimerDisplay.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@223: iTableLayoutPanel.Enabled = true; StephaneLenclud@223: 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@223: if (cds.Brightness < iDisplay.MinBrightness() || cds.Brightness > iDisplay.MaxBrightness()) StephaneLenclud@223: { StephaneLenclud@223: //Brightness out of range, this can occur when using auto-detect StephaneLenclud@223: //Use max brightness instead StephaneLenclud@223: trackBarBrightness.Value = iDisplay.MaxBrightness(); StephaneLenclud@223: iDisplay.SetBrightness(iDisplay.MaxBrightness()); StephaneLenclud@223: } StephaneLenclud@223: else StephaneLenclud@223: { StephaneLenclud@223: trackBarBrightness.Value = cds.Brightness; StephaneLenclud@223: iDisplay.SetBrightness(cds.Brightness); StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: //Try compute the steps to something that makes sense StephaneLenclud@223: trackBarBrightness.LargeChange = Math.Max(1, (iDisplay.MaxBrightness() - iDisplay.MinBrightness())/5); sl@48: trackBarBrightness.SmallChange = 1; StephaneLenclud@223: 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@223: StephaneLenclud@223: //Check if Volume Label is supported. To date only MDM166AA supports that crap :) StephaneLenclud@223: checkBoxShowVolumeLabel.Enabled = iDisplay.IconCount(MiniDisplay.IconType.VolumeLabel) > 0; StephaneLenclud@223: StephaneLenclud@223: if (cds.ShowVolumeLabel) StephaneLenclud@223: { StephaneLenclud@135: iDisplay.SetIconOn(MiniDisplay.IconType.VolumeLabel); StephaneLenclud@223: } StephaneLenclud@223: else StephaneLenclud@223: { StephaneLenclud@135: iDisplay.SetIconOff(MiniDisplay.IconType.VolumeLabel); StephaneLenclud@223: } 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@223: //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@223: iTableLayoutPanel.Enabled = false; StephaneLenclud@223: 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@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: private void checkBoxShowVolumeLabel_CheckedChanged(object sender, EventArgs e) StephaneLenclud@223: { StephaneLenclud@223: cds.ShowVolumeLabel = checkBoxShowVolumeLabel.Checked; StephaneLenclud@223: Properties.Settings.Default.Save(); StephaneLenclud@223: UpdateStatus(); StephaneLenclud@223: } sl@13: sl@9: private void checkBoxShowBorders_CheckedChanged(object sender, EventArgs e) sl@9: { sl@16: //Save our show borders setting StephaneLenclud@223: iTableLayoutPanel.CellBorderStyle = (checkBoxShowBorders.Checked StephaneLenclud@223: ? TableLayoutPanelCellBorderStyle.Single StephaneLenclud@223: : TableLayoutPanelCellBorderStyle.None); sl@48: cds.ShowBorders = checkBoxShowBorders.Checked; sl@9: Properties.Settings.Default.Save(); sl@57: CheckFontHeight(); sl@9: } sl@13: StephaneLenclud@194: private void checkBoxAutoStart_CheckedChanged(object sender, EventArgs e) StephaneLenclud@223: { StephaneLenclud@223: iStartupManager.Startup = checkBoxAutoStart.Checked; StephaneLenclud@223: } 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@273: //TODO: discard other CSCore audio objects StephaneLenclud@273: StopAudioVisualization(); StephaneLenclud@167: iCecManager.Stop(); StephaneLenclud@223: iNetworkManager.Dispose(); StephaneLenclud@223: 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: ( StephaneLenclud@223: typeof(Session), StephaneLenclud@223: new Uri[] {new Uri("net.tcp://localhost:8001/")} sl@17: ); sl@17: StephaneLenclud@223: iServiceHost.AddServiceEndpoint(typeof(IService), new NetTcpBinding(SecurityMode.None, true), StephaneLenclud@223: "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: { StephaneLenclud@223: if ( StephaneLenclud@223: MessageBox.Show("Force exit?", "Waiting for clients...", MessageBoxButtons.YesNo, StephaneLenclud@223: 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); StephaneLenclud@223: 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@226: Program.iFormMain.iTreeViewClients.Nodes.Remove( StephaneLenclud@226: Program.iFormMain.iTreeViewClients.Nodes.Find(client, false)[0]); sl@21: } sl@21: } sl@97: StephaneLenclud@223: if (iClients.Count == 0) StephaneLenclud@223: { StephaneLenclud@223: ClearLayout(); StephaneLenclud@223: } sl@21: } sl@21: StephaneLenclud@223: /// StephaneLenclud@223: /// Just remove all our fields. StephaneLenclud@223: /// StephaneLenclud@223: private void ClearLayout() StephaneLenclud@223: { StephaneLenclud@223: iTableLayoutPanel.Controls.Clear(); StephaneLenclud@223: iTableLayoutPanel.RowStyles.Clear(); StephaneLenclud@223: iTableLayoutPanel.ColumnStyles.Clear(); StephaneLenclud@223: iCurrentClientData = null; StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: /// StephaneLenclud@223: /// Just launch a demo client. StephaneLenclud@223: /// StephaneLenclud@223: private void StartNewClient(string aTopText = "", string aBottomText = "") StephaneLenclud@223: { StephaneLenclud@223: Thread clientThread = new Thread(SharpDisplayClient.Program.MainWithParams); StephaneLenclud@223: SharpDisplayClient.StartParams myParams = new SharpDisplayClient.StartParams( StephaneLenclud@223: new Point(this.Right, this.Top), aTopText, aBottomText); StephaneLenclud@223: clientThread.Start(myParams); StephaneLenclud@223: BringToFront(); StephaneLenclud@223: } 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@225: Thread clientThread = new Thread(SharpDisplayClientIdle.Program.MainWithParams); StephaneLenclud@225: SharpDisplayClientIdle.StartParams myParams = StephaneLenclud@225: new SharpDisplayClientIdle.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@223: 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@270: iTimerDisplay.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@270: iTimerDisplay.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 StephaneLenclud@270: iTimerDisplay.Enabled = !iTimerDisplay.Enabled; StephaneLenclud@187: UpdateSuspendButton(); StephaneLenclud@187: } StephaneLenclud@187: StephaneLenclud@187: private void UpdateSuspendButton() StephaneLenclud@187: { StephaneLenclud@270: if (!iTimerDisplay.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@223: 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@223: SetCurrentClient(sessionId, true); StephaneLenclud@141: } StephaneLenclud@223: 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); StephaneLenclud@223: 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); StephaneLenclud@226: Program.iFormMain.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); StephaneLenclud@223: 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 StephaneLenclud@226: if (Program.iFormMain.iClients.Keys.Contains(aSessionId)) sl@30: { StephaneLenclud@226: Program.iFormMain.iClients.Remove(aSessionId); StephaneLenclud@226: Program.iFormMain.iTreeViewClients.Nodes.Remove( StephaneLenclud@226: Program.iFormMain.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(); StephaneLenclud@223: if (newCurrentClient != null) Stephane@186: { Stephane@186: SetCurrentClient(newCurrentClient.SessionId, true); StephaneLenclud@223: } Stephane@186: } Stephane@186: Stephane@186: if (iClients.Count == 0) StephaneLenclud@223: { StephaneLenclud@223: //Clear our screen when last client disconnects StephaneLenclud@223: ClearLayout(); StephaneLenclud@223: StephaneLenclud@223: if (iClosing) StephaneLenclud@223: { StephaneLenclud@223: //We were closing our form StephaneLenclud@223: //All clients are now closed StephaneLenclud@223: //Just resume our close operation StephaneLenclud@223: iClosing = false; StephaneLenclud@223: Close(); StephaneLenclud@223: } StephaneLenclud@223: } 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); StephaneLenclud@223: 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); StephaneLenclud@223: 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@223: { 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@223: Control ctrl = iTableLayoutPanel.GetControlFromPosition(tableField.Column, tableField.Row); StephaneLenclud@176: if (aField.IsTextField && ctrl is MarqueeLabel) sl@79: { StephaneLenclud@274: TextField textField = (TextField)aField; sl@75: //Text field control already in place, just change the text StephaneLenclud@274: 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@274: 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@274: PictureBox pictureBox = (PictureBox)ctrl; StephaneLenclud@172: pictureBox.Image = bitmapField.Bitmap; sl@75: } StephaneLenclud@274: else if (aField is AudioVisualizerField && ctrl is PictureBox) StephaneLenclud@274: { StephaneLenclud@274: contentChanged = false; // Since nothing was changed StephaneLenclud@274: } sl@68: else sl@68: { StephaneLenclud@141: layoutChanged = true; sl@68: } sl@30: } StephaneLenclud@141: } StephaneLenclud@141: else StephaneLenclud@223: { 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); StephaneLenclud@223: 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); StephaneLenclud@223: 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@223: 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(); StephaneLenclud@223: 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@223: 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@223: string text = ""; StephaneLenclud@223: RecordingField recField = new RecordingField(); StephaneLenclud@180: foreach (var client in iClients) StephaneLenclud@180: { StephaneLenclud@223: RecordingField rec = (RecordingField) client.Value.FindSameFieldAs(recField); StephaneLenclud@223: 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@223: 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@223: iRecordingNotification.Text = Truncate(text, 63); StephaneLenclud@183: StephaneLenclud@180: //Change visibility of notification if needed StephaneLenclud@180: if (iRecordingNotification.Visible != activeRecording) StephaneLenclud@223: { 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@223: } 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); StephaneLenclud@223: 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@223: 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@274: else if (field is AudioVisualizerField) StephaneLenclud@274: { StephaneLenclud@274: textsRoot.Nodes.Add(new TreeNode("[Audio Visualizer]")); StephaneLenclud@274: } StephaneLenclud@172: else if (field.IsRecordingField) StephaneLenclud@172: { StephaneLenclud@223: 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@223: 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@223: if (aClient == null) StephaneLenclud@223: { StephaneLenclud@223: //Just drop it StephaneLenclud@223: return; StephaneLenclud@223: } 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@223: 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: { StephaneLenclud@223: 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@274: 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@274: BitmapField field = (BitmapField)aField; StephaneLenclud@172: picture.Image = field.Bitmap; sl@68: // sl@68: control = picture; sl@68: } StephaneLenclud@274: else if (aField is AudioVisualizerField) StephaneLenclud@274: { StephaneLenclud@274: //Create picture box StephaneLenclud@274: PictureBox picture = new PictureBox(); StephaneLenclud@274: picture.AutoSize = true; StephaneLenclud@274: picture.BackColor = System.Drawing.Color.Transparent; StephaneLenclud@274: picture.Dock = System.Windows.Forms.DockStyle.Fill; StephaneLenclud@274: picture.Location = new System.Drawing.Point(1, 1); StephaneLenclud@274: picture.Margin = new System.Windows.Forms.Padding(0); StephaneLenclud@274: picture.Name = "pictureBox" + aField; StephaneLenclud@274: control = picture; StephaneLenclud@274: } StephaneLenclud@274: StephaneLenclud@172: //TODO: Handle recording field? sl@68: sl@69: return control; sl@68: } sl@68: StephaneLenclud@223: /// StephaneLenclud@223: /// Called when the user selected a new display type. StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// sl@46: private void comboBoxDisplayType_SelectedIndexChanged(object sender, EventArgs e) sl@46: { StephaneLenclud@223: //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@223: //Try re-opening the display connection if we were already connected. StephaneLenclud@223: //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: { StephaneLenclud@270: iTimerDisplay.Interval = interval; StephaneLenclud@270: cds.TimerInterval = iTimerDisplay.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@223: //We need to recreate our layout for that change to take effect StephaneLenclud@223: UpdateTableLayoutPanel(iCurrentClientData); sl@100: } sl@100: } sl@100: } sl@100: StephaneLenclud@106: StephaneLenclud@223: private void maskedTextBoxScrollingSpeed_TextChanged(object sender, EventArgs e) StephaneLenclud@223: { StephaneLenclud@223: if (maskedTextBoxScrollingSpeed.Text != "") StephaneLenclud@223: { StephaneLenclud@223: int scrollingSpeed = Convert.ToInt32(maskedTextBoxScrollingSpeed.Text); StephaneLenclud@223: StephaneLenclud@223: if (scrollingSpeed > 0) StephaneLenclud@223: { StephaneLenclud@223: cds.ScrollingSpeedInPixelsPerSecond = scrollingSpeed; StephaneLenclud@223: Properties.Settings.Default.Save(); StephaneLenclud@223: //We need to recreate our layout for that change to take effect StephaneLenclud@223: UpdateTableLayoutPanel(iCurrentClientData); StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: } 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@223: //Update our text fields StephaneLenclud@223: foreach (MarqueeLabel ctrl in iTableLayoutPanel.Controls) StephaneLenclud@223: { StephaneLenclud@223: ctrl.Separator = cds.Separator; StephaneLenclud@223: } 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@223: ShowClock(); sl@53: } sl@53: sl@53: private void buttonHideClock_Click(object sender, EventArgs e) sl@53: { StephaneLenclud@223: 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@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: void ShowClock() StephaneLenclud@223: { StephaneLenclud@223: if (!iDisplay.IsOpen()) StephaneLenclud@223: { StephaneLenclud@223: return; StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock StephaneLenclud@223: iSkipFrameRendering = true; StephaneLenclud@223: //Clear our screen StephaneLenclud@223: iDisplay.Clear(); StephaneLenclud@223: iDisplay.SwapBuffers(); StephaneLenclud@223: //Then show our clock StephaneLenclud@223: iDisplay.ShowClock(); StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: void HideClock() StephaneLenclud@223: { StephaneLenclud@223: if (!iDisplay.IsOpen()) StephaneLenclud@223: { StephaneLenclud@223: return; StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: //Devices like MDM166AA don't support windowing and frame rendering must be stopped while showing our clock StephaneLenclud@223: iSkipFrameRendering = false; StephaneLenclud@223: iDisplay.HideClock(); StephaneLenclud@223: } 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: { StephaneLenclud@223: MessageBox.Show( StephaneLenclud@223: "The new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " + StephaneLenclud@223: dde.Message); sl@88: return; sl@88: } sl@88: catch (InvalidDeploymentException ide) sl@88: { StephaneLenclud@223: MessageBox.Show( StephaneLenclud@223: "Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " + StephaneLenclud@223: ide.Message); sl@88: return; sl@88: } sl@88: catch (InvalidOperationException ioe) sl@88: { StephaneLenclud@223: MessageBox.Show( StephaneLenclud@223: "This application cannot be updated. It is likely not a ClickOnce application. Error: " + StephaneLenclud@223: ioe.Message); sl@88: return; sl@88: } sl@88: StephaneLenclud@223: if (info.UpdateAvailable) StephaneLenclud@223: { StephaneLenclud@223: Boolean doUpdate = true; StephaneLenclud@223: StephaneLenclud@223: if (!info.IsUpdateRequired) StephaneLenclud@223: { StephaneLenclud@223: DialogResult dr = StephaneLenclud@223: MessageBox.Show("An update is available. Would you like to update the application now?", StephaneLenclud@223: "Update Available", MessageBoxButtons.OKCancel); StephaneLenclud@223: if (!(DialogResult.OK == dr)) StephaneLenclud@223: { StephaneLenclud@223: doUpdate = false; StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: else StephaneLenclud@223: { StephaneLenclud@223: // Display a message that the application MUST reboot. Display the minimum required version. StephaneLenclud@223: MessageBox.Show("This application has detected a mandatory update from your current " + StephaneLenclud@223: "version to version " + info.MinimumRequiredVersion.ToString() + StephaneLenclud@223: ". The application will now install the update and restart.", StephaneLenclud@223: "Update Available", MessageBoxButtons.OK, StephaneLenclud@223: MessageBoxIcon.Information); StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: if (doUpdate) StephaneLenclud@223: { StephaneLenclud@223: try StephaneLenclud@223: { StephaneLenclud@223: ad.Update(); StephaneLenclud@223: MessageBox.Show("The application has been upgraded, and will now restart."); StephaneLenclud@223: Application.Restart(); StephaneLenclud@223: } StephaneLenclud@223: catch (DeploymentDownloadException dde) StephaneLenclud@223: { StephaneLenclud@223: MessageBox.Show( StephaneLenclud@223: "Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + StephaneLenclud@223: dde); StephaneLenclud@223: return; StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: else StephaneLenclud@223: { StephaneLenclud@223: MessageBox.Show("You are already running the latest version.", "Application up-to-date"); StephaneLenclud@223: } sl@88: } sl@88: } sl@92: sl@94: StephaneLenclud@223: /// StephaneLenclud@223: /// Used to StephaneLenclud@223: /// StephaneLenclud@223: private void SysTrayHideShow() StephaneLenclud@223: { StephaneLenclud@223: Visible = !Visible; StephaneLenclud@223: if (Visible) StephaneLenclud@223: { StephaneLenclud@223: Activate(); StephaneLenclud@223: WindowState = FormWindowState.Normal; StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: /// StephaneLenclud@223: /// Use to handle minimize events. StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: private void MainForm_SizeChanged(object sender, EventArgs e) StephaneLenclud@223: { StephaneLenclud@223: if (WindowState == FormWindowState.Minimized && Properties.Settings.Default.MinimizeToTray) StephaneLenclud@223: { StephaneLenclud@223: if (Visible) StephaneLenclud@223: { StephaneLenclud@223: SysTrayHideShow(); StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: private void tableLayoutPanel_SizeChanged(object sender, EventArgs e) StephaneLenclud@223: { StephaneLenclud@223: //Our table layout size has changed which means our display size has changed. StephaneLenclud@223: //We need to re-create our bitmap. StephaneLenclud@223: iCreateBitmap = true; StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: /// StephaneLenclud@167: /// Broadcast messages to subscribers. StephaneLenclud@167: /// StephaneLenclud@167: /// StephaneLenclud@167: protected override void WndProc(ref Message aMessage) StephaneLenclud@167: { StephaneLenclud@223: if (OnWndProc != null) StephaneLenclud@167: { StephaneLenclud@167: OnWndProc(ref aMessage); StephaneLenclud@167: } StephaneLenclud@223: StephaneLenclud@167: base.WndProc(ref aMessage); StephaneLenclud@167: } StephaneLenclud@168: Stephane@252: private void iCheckBoxCecEnabled_CheckedChanged(object sender, EventArgs e) StephaneLenclud@168: { 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: /// StephaneLenclud@168: /// StephaneLenclud@168: /// StephaneLenclud@168: private void ResetCec() StephaneLenclud@168: { StephaneLenclud@223: 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@223: Properties.Settings.Default.CecHdmiPort); StephaneLenclud@206: Stephane@207: SetupCecLogLevel(); Stephane@207: } Stephane@207: } StephaneLenclud@206: Stephane@207: /// Stephane@207: /// Stephane@207: /// StephaneLenclud@255: private async void ResetHarmonyAsync(bool aForceAuth=false) StephaneLenclud@233: { StephaneLenclud@233: // ConnectAsync already if we have an existing session cookie Stephane@252: if (Properties.Settings.Default.HarmonyEnabled) StephaneLenclud@233: { StephaneLenclud@233: try StephaneLenclud@233: { StephaneLenclud@255: iButtonHarmonyConnect.Enabled = false; Stephane@252: await ConnectHarmonyAsync(aForceAuth); StephaneLenclud@233: } Stephane@252: catch (Exception ex) StephaneLenclud@233: { StephaneLenclud@253: Trace.WriteLine("Exception thrown by ConnectHarmonyAsync"); StephaneLenclud@253: Trace.WriteLine(ex.ToString()); StephaneLenclud@233: } StephaneLenclud@255: finally StephaneLenclud@255: { StephaneLenclud@255: iButtonHarmonyConnect.Enabled = true; StephaneLenclud@255: } StephaneLenclud@233: } StephaneLenclud@233: } StephaneLenclud@233: StephaneLenclud@233: /// StephaneLenclud@233: /// StephaneLenclud@233: /// Stephane@252: /// Stephane@252: /// Stephane@252: private void iButtonHarmonyConnect_Click(object sender, EventArgs e) Stephane@252: { Stephane@252: // User is explicitaly trying to connect Stephane@252: //Reset Harmony Hub connection forcing authentication StephaneLenclud@255: ResetHarmonyAsync(true); Stephane@252: } Stephane@252: Stephane@252: /// Stephane@252: /// Stephane@252: /// Stephane@252: /// Stephane@252: /// Stephane@252: private void iCheckBoxHarmonyEnabled_CheckedChanged(object sender, EventArgs e) Stephane@252: { Stephane@252: iButtonHarmonyConnect.Enabled = iCheckBoxHarmonyEnabled.Checked; Stephane@252: } Stephane@252: Stephane@252: /// Stephane@252: /// Stephane@252: /// 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) StephaneLenclud@223: iCecManager.Client.LogLevel |= (int) CecLogLevel.Error; StephaneLenclud@206: Stephane@207: if (checkBoxCecLogWarning.Checked) StephaneLenclud@223: iCecManager.Client.LogLevel |= (int) CecLogLevel.Warning; StephaneLenclud@206: Stephane@207: if (checkBoxCecLogNotice.Checked) StephaneLenclud@223: iCecManager.Client.LogLevel |= (int) CecLogLevel.Notice; StephaneLenclud@206: Stephane@207: if (checkBoxCecLogTraffic.Checked) StephaneLenclud@223: iCecManager.Client.LogLevel |= (int) CecLogLevel.Traffic; StephaneLenclud@206: Stephane@207: if (checkBoxCecLogDebug.Checked) StephaneLenclud@223: 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@223: StephaneLenclud@218: StephaneLenclud@219: StephaneLenclud@219: StephaneLenclud@217: /// StephaneLenclud@219: /// Get the current event based on event tree view selection. StephaneLenclud@217: /// StephaneLenclud@217: /// StephaneLenclud@234: private Ear.Event CurrentEvent() StephaneLenclud@217: { StephaneLenclud@217: //Walk up the tree from the selected node to find our event StephaneLenclud@217: TreeNode node = iTreeViewEvents.SelectedNode; StephaneLenclud@234: Ear.Event selectedEvent = null; StephaneLenclud@217: while (node != null) StephaneLenclud@217: { StephaneLenclud@234: if (node.Tag is Ear.Event) StephaneLenclud@217: { StephaneLenclud@234: selectedEvent = (Ear.Event) node.Tag; StephaneLenclud@217: break; StephaneLenclud@217: } StephaneLenclud@217: node = node.Parent; StephaneLenclud@217: } StephaneLenclud@217: StephaneLenclud@217: return selectedEvent; StephaneLenclud@217: } StephaneLenclud@217: StephaneLenclud@217: /// StephaneLenclud@219: /// Get the current action based on event tree view selection StephaneLenclud@217: /// StephaneLenclud@217: /// StephaneLenclud@234: private Ear.Action CurrentAction() StephaneLenclud@217: { StephaneLenclud@217: TreeNode node = iTreeViewEvents.SelectedNode; StephaneLenclud@234: if (node != null && node.Tag is Ear.Action) StephaneLenclud@217: { StephaneLenclud@234: return (Ear.Action) node.Tag; StephaneLenclud@217: } StephaneLenclud@217: StephaneLenclud@217: return null; StephaneLenclud@217: } StephaneLenclud@217: StephaneLenclud@217: /// StephaneLenclud@217: /// StephaneLenclud@217: /// StephaneLenclud@265: /// StephaneLenclud@265: private Ear.Object CurrentEarObject() StephaneLenclud@265: { StephaneLenclud@265: Ear.Action a = CurrentAction(); StephaneLenclud@265: Ear.Event e = CurrentEvent(); StephaneLenclud@265: StephaneLenclud@265: if (a != null) StephaneLenclud@265: { StephaneLenclud@265: return a; StephaneLenclud@265: } StephaneLenclud@265: StephaneLenclud@265: return e; StephaneLenclud@265: } StephaneLenclud@265: StephaneLenclud@265: /// StephaneLenclud@265: /// Get the current action based on event tree view selection StephaneLenclud@265: /// StephaneLenclud@265: /// StephaneLenclud@265: private Ear.Object CurrentEarParent() StephaneLenclud@265: { StephaneLenclud@265: TreeNode node = iTreeViewEvents.SelectedNode; StephaneLenclud@265: if (node == null || node.Parent == null) StephaneLenclud@265: { StephaneLenclud@265: return null; StephaneLenclud@265: } StephaneLenclud@265: StephaneLenclud@265: if (node.Parent.Tag is Ear.Object) StephaneLenclud@265: { StephaneLenclud@265: return (Ear.Object)node.Parent.Tag; StephaneLenclud@265: } StephaneLenclud@265: StephaneLenclud@265: if (node.Parent.Parent != null && node.Parent.Parent.Tag is Ear.Object) StephaneLenclud@265: { StephaneLenclud@265: //Can be the case for events StephaneLenclud@265: return (Ear.Object)node.Parent.Parent.Tag; StephaneLenclud@265: } StephaneLenclud@265: StephaneLenclud@265: return null; StephaneLenclud@265: } StephaneLenclud@265: StephaneLenclud@265: StephaneLenclud@265: /// StephaneLenclud@265: /// StephaneLenclud@265: /// StephaneLenclud@217: /// StephaneLenclud@217: /// StephaneLenclud@227: private void buttonActionAdd_Click(object sender, EventArgs e) StephaneLenclud@211: { StephaneLenclud@265: Ear.Object parent = CurrentEarObject(); StephaneLenclud@265: if (parent == null ) Stephane@212: { StephaneLenclud@265: //We did not find a corresponding event or action StephaneLenclud@211: return; StephaneLenclud@211: } StephaneLenclud@211: StephaneLenclud@234: FormEditObject ea = new FormEditObject(); StephaneLenclud@228: ea.Text = "Add action"; StephaneLenclud@211: DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea); StephaneLenclud@211: if (res == DialogResult.OK) StephaneLenclud@211: { StephaneLenclud@265: parent.Objects.Add(ea.Object); Stephane@212: Properties.Settings.Default.Save(); StephaneLenclud@266: // We want to select the parent so that one can easily add another action to the same collection StephaneLenclud@266: PopulateTreeViewEvents(parent); StephaneLenclud@211: } StephaneLenclud@211: } StephaneLenclud@214: StephaneLenclud@217: /// StephaneLenclud@217: /// StephaneLenclud@217: /// StephaneLenclud@217: /// StephaneLenclud@217: /// StephaneLenclud@227: private void buttonActionEdit_Click(object sender, EventArgs e) StephaneLenclud@227: { StephaneLenclud@234: Ear.Action selectedAction = CurrentAction(); StephaneLenclud@265: Ear.Object parent = CurrentEarParent() StephaneLenclud@265: ; StephaneLenclud@265: if (parent == null || selectedAction == null) StephaneLenclud@227: { StephaneLenclud@265: //We did not find a corresponding parent StephaneLenclud@227: return; StephaneLenclud@227: } StephaneLenclud@227: StephaneLenclud@234: FormEditObject ea = new FormEditObject(); StephaneLenclud@228: ea.Text = "Edit action"; StephaneLenclud@231: ea.Object = selectedAction; StephaneLenclud@227: int actionIndex = iTreeViewEvents.SelectedNode.Index; StephaneLenclud@227: DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea); StephaneLenclud@227: if (res == DialogResult.OK) StephaneLenclud@227: { StephaneLenclud@265: //Make sure we keep the same children as before StephaneLenclud@265: ea.Object.Objects = parent.Objects[actionIndex].Objects; StephaneLenclud@227: //Update our action StephaneLenclud@265: parent.Objects[actionIndex]=ea.Object; StephaneLenclud@227: //Save and rebuild our event tree view StephaneLenclud@227: Properties.Settings.Default.Save(); StephaneLenclud@266: PopulateTreeViewEvents(ea.Object); StephaneLenclud@227: } StephaneLenclud@227: } StephaneLenclud@227: StephaneLenclud@227: /// StephaneLenclud@227: /// StephaneLenclud@227: /// StephaneLenclud@227: /// StephaneLenclud@227: /// StephaneLenclud@227: private void buttonActionDelete_Click(object sender, EventArgs e) StephaneLenclud@214: { StephaneLenclud@234: Ear.Action action = CurrentAction(); StephaneLenclud@214: if (action == null) StephaneLenclud@214: { StephaneLenclud@214: //Must select action node StephaneLenclud@214: return; StephaneLenclud@214: } StephaneLenclud@214: StephaneLenclud@235: Properties.Settings.Default.EarManager.RemoveAction(action); StephaneLenclud@214: Properties.Settings.Default.Save(); StephaneLenclud@260: PopulateTreeViewEvents(); StephaneLenclud@214: } StephaneLenclud@217: StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@227: private void buttonActionTest_Click(object sender, EventArgs e) StephaneLenclud@223: { StephaneLenclud@234: Ear.Action a = CurrentAction(); StephaneLenclud@223: if (a != null) StephaneLenclud@223: { StephaneLenclud@231: a.Test(); StephaneLenclud@223: } StephaneLenclud@223: iTreeViewEvents.Focus(); StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: private void buttonActionMoveUp_Click(object sender, EventArgs e) StephaneLenclud@223: { StephaneLenclud@234: Ear.Action a = CurrentAction(); StephaneLenclud@223: if (a == null || StephaneLenclud@223: //Action already at the top of the list StephaneLenclud@223: iTreeViewEvents.SelectedNode.Index == 0) StephaneLenclud@223: { StephaneLenclud@223: return; StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: //Swap actions in event's action list StephaneLenclud@265: Ear.Object parent = CurrentEarParent(); StephaneLenclud@223: int currentIndex = iTreeViewEvents.SelectedNode.Index; StephaneLenclud@265: Ear.Action movingUp = parent.Objects[currentIndex] as Ear.Action; StephaneLenclud@265: Ear.Action movingDown = parent.Objects[currentIndex-1] as Ear.Action; StephaneLenclud@265: parent.Objects[currentIndex] = movingDown; StephaneLenclud@265: parent.Objects[currentIndex-1] = movingUp; StephaneLenclud@223: StephaneLenclud@223: //Save and populate our tree again StephaneLenclud@223: Properties.Settings.Default.Save(); StephaneLenclud@266: PopulateTreeViewEvents(a); StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: /// StephaneLenclud@223: private void buttonActionMoveDown_Click(object sender, EventArgs e) StephaneLenclud@223: { StephaneLenclud@234: Ear.Action a = CurrentAction(); StephaneLenclud@223: if (a == null || StephaneLenclud@223: //Action already at the bottom of the list StephaneLenclud@223: iTreeViewEvents.SelectedNode.Index == iTreeViewEvents.SelectedNode.Parent.Nodes.Count - 1) StephaneLenclud@223: { StephaneLenclud@223: return; StephaneLenclud@223: } StephaneLenclud@223: StephaneLenclud@223: //Swap actions in event's action list StephaneLenclud@265: Ear.Object parent = CurrentEarParent(); StephaneLenclud@223: int currentIndex = iTreeViewEvents.SelectedNode.Index; StephaneLenclud@265: Ear.Action movingDown = parent.Objects[currentIndex] as Ear.Action; StephaneLenclud@265: Ear.Action movingUp = parent.Objects[currentIndex + 1] as Ear.Action; StephaneLenclud@265: parent.Objects[currentIndex] = movingUp; StephaneLenclud@265: parent.Objects[currentIndex + 1] = movingDown; StephaneLenclud@223: StephaneLenclud@223: //Save and populate our tree again StephaneLenclud@223: Properties.Settings.Default.Save(); StephaneLenclud@266: PopulateTreeViewEvents(a); StephaneLenclud@217: } StephaneLenclud@227: StephaneLenclud@227: StephaneLenclud@227: /// StephaneLenclud@229: /// StephaneLenclud@229: /// StephaneLenclud@229: /// StephaneLenclud@229: /// StephaneLenclud@229: private void buttonEventTest_Click(object sender, EventArgs e) StephaneLenclud@229: { StephaneLenclud@234: Ear.Event earEvent = CurrentEvent(); StephaneLenclud@229: if (earEvent != null) StephaneLenclud@229: { StephaneLenclud@231: earEvent.Test(); StephaneLenclud@229: } StephaneLenclud@229: } StephaneLenclud@229: StephaneLenclud@229: /// StephaneLenclud@227: /// Manages events and actions buttons according to selected item in event tree. StephaneLenclud@227: /// StephaneLenclud@227: /// StephaneLenclud@227: /// StephaneLenclud@227: private void iTreeViewEvents_AfterSelect(object sender, TreeViewEventArgs e) StephaneLenclud@227: { StephaneLenclud@231: UpdateEventView(); StephaneLenclud@231: } StephaneLenclud@231: StephaneLenclud@231: /// StephaneLenclud@231: /// StephaneLenclud@231: /// StephaneLenclud@231: private void UpdateEventView() StephaneLenclud@231: { StephaneLenclud@231: //One can always add an event StephaneLenclud@231: buttonEventAdd.Enabled = true; StephaneLenclud@231: StephaneLenclud@227: //Enable buttons according to selected item StephaneLenclud@229: buttonActionAdd.Enabled = StephaneLenclud@229: buttonEventTest.Enabled = StephaneLenclud@231: buttonEventDelete.Enabled = StephaneLenclud@231: buttonEventEdit.Enabled = StephaneLenclud@229: CurrentEvent() != null; StephaneLenclud@227: StephaneLenclud@234: Ear.Action currentAction = CurrentAction(); StephaneLenclud@227: //If an action is selected enable the following buttons StephaneLenclud@227: buttonActionTest.Enabled = StephaneLenclud@227: buttonActionDelete.Enabled = StephaneLenclud@227: buttonActionMoveUp.Enabled = StephaneLenclud@227: buttonActionMoveDown.Enabled = StephaneLenclud@231: buttonActionEdit.Enabled = StephaneLenclud@227: currentAction != null; StephaneLenclud@227: StephaneLenclud@227: if (currentAction != null) StephaneLenclud@227: { StephaneLenclud@227: //If an action is selected enable move buttons if needed StephaneLenclud@227: buttonActionMoveUp.Enabled = iTreeViewEvents.SelectedNode.Index != 0; StephaneLenclud@227: buttonActionMoveDown.Enabled = iTreeViewEvents.SelectedNode.Index < StephaneLenclud@227: iTreeViewEvents.SelectedNode.Parent.Nodes.Count - 1; StephaneLenclud@227: } StephaneLenclud@227: } StephaneLenclud@227: StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@231: private void buttonEventAdd_Click(object sender, EventArgs e) StephaneLenclud@231: { StephaneLenclud@234: FormEditObject ea = new FormEditObject(); StephaneLenclud@231: ea.Text = "Add event"; StephaneLenclud@231: DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea); StephaneLenclud@231: if (res == DialogResult.OK) StephaneLenclud@231: { StephaneLenclud@235: Properties.Settings.Default.EarManager.Events.Add(ea.Object); StephaneLenclud@231: Properties.Settings.Default.Save(); StephaneLenclud@266: PopulateTreeViewEvents(ea.Object); StephaneLenclud@231: } StephaneLenclud@231: } StephaneLenclud@231: StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@231: private void buttonEventDelete_Click(object sender, EventArgs e) StephaneLenclud@231: { StephaneLenclud@234: Ear.Event currentEvent = CurrentEvent(); StephaneLenclud@231: if (currentEvent == null) StephaneLenclud@231: { StephaneLenclud@231: //Must select action node StephaneLenclud@231: return; StephaneLenclud@231: } StephaneLenclud@231: StephaneLenclud@235: Properties.Settings.Default.EarManager.Events.Remove(currentEvent); StephaneLenclud@231: Properties.Settings.Default.Save(); StephaneLenclud@260: PopulateTreeViewEvents(); StephaneLenclud@231: } StephaneLenclud@231: StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@231: private void buttonEventEdit_Click(object sender, EventArgs e) StephaneLenclud@231: { StephaneLenclud@234: Ear.Event selectedEvent = CurrentEvent(); StephaneLenclud@231: if (selectedEvent == null) StephaneLenclud@231: { StephaneLenclud@231: //We did not find a corresponding event StephaneLenclud@231: return; StephaneLenclud@231: } StephaneLenclud@231: StephaneLenclud@234: FormEditObject ea = new FormEditObject(); StephaneLenclud@231: ea.Text = "Edit event"; StephaneLenclud@231: ea.Object = selectedEvent; StephaneLenclud@248: int index = iTreeViewEvents.SelectedNode.Index; StephaneLenclud@231: DialogResult res = CodeProject.Dialog.DlgBox.ShowDialog(ea); StephaneLenclud@231: if (res == DialogResult.OK) StephaneLenclud@248: { StephaneLenclud@248: //Make sure we keep the same actions as before StephaneLenclud@265: ea.Object.Objects = Properties.Settings.Default.EarManager.Events[index].Objects; StephaneLenclud@248: //Update our event StephaneLenclud@248: Properties.Settings.Default.EarManager.Events[index] = ea.Object; StephaneLenclud@231: //Save and rebuild our event tree view StephaneLenclud@231: Properties.Settings.Default.Save(); StephaneLenclud@266: PopulateTreeViewEvents(ea.Object); StephaneLenclud@231: } StephaneLenclud@231: } StephaneLenclud@231: StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@231: private void iTreeViewEvents_Leave(object sender, EventArgs e) StephaneLenclud@231: { StephaneLenclud@231: //Make sure our event tree never looses focus StephaneLenclud@231: ((TreeView) sender).Focus(); StephaneLenclud@231: } StephaneLenclud@233: StephaneLenclud@250: /// StephaneLenclud@256: /// Called whenever we loose connection with our HarmonyHub. StephaneLenclud@256: /// StephaneLenclud@256: /// St?phane@271: private void HarmonyConnectionClosed(object aSender, bool aClosedByServer) StephaneLenclud@256: { St?phane@271: if (aClosedByServer) St?phane@271: { St?phane@271: //Try reconnect then StephaneLenclud@256: #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed St?phane@271: BeginInvoke(new MethodInvoker(delegate () { ResetHarmonyAsync(); })); StephaneLenclud@256: #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed St?phane@271: } StephaneLenclud@256: } StephaneLenclud@256: StephaneLenclud@270: StephaneLenclud@270: StephaneLenclud@270: int iHarmonyReconnectTries = 0; StephaneLenclud@270: const int KHarmonyMaxReconnectTries = 10; StephaneLenclud@270: StephaneLenclud@256: /// StephaneLenclud@250: /// StephaneLenclud@250: /// StephaneLenclud@250: /// Stephane@252: private async Task ConnectHarmonyAsync(bool aForceAuth=false) StephaneLenclud@233: { StephaneLenclud@236: if (Program.HarmonyClient != null) StephaneLenclud@236: { StephaneLenclud@254: await Program.HarmonyClient.CloseAsync(); StephaneLenclud@236: } StephaneLenclud@236: StephaneLenclud@270: bool success = false; StephaneLenclud@270: StephaneLenclud@236: //Reset Harmony client & config StephaneLenclud@236: Program.HarmonyClient = null; StephaneLenclud@236: Program.HarmonyConfig = null; StephaneLenclud@270: iTreeViewHarmony.Nodes.Clear(); StephaneLenclud@236: StephaneLenclud@253: Trace.WriteLine("Harmony: Connecting... "); StephaneLenclud@256: //First create our client and login StephaneLenclud@259: //Tip: Set keep-alive to false when testing reconnection process StephaneLenclud@259: Program.HarmonyClient = new HarmonyHub.Client(iTextBoxHarmonyHubAddress.Text, true); St?phane@271: Program.HarmonyClient.OnConnectionClosed += HarmonyConnectionClosed; StephaneLenclud@266: StephaneLenclud@266: string authToken = Properties.Settings.Default.LogitechAuthToken; StephaneLenclud@266: if (!string.IsNullOrEmpty(authToken) && !aForceAuth) StephaneLenclud@233: { StephaneLenclud@266: Trace.WriteLine("Harmony: Reusing token: {0}", authToken); StephaneLenclud@270: success = await Program.HarmonyClient.TryOpenAsync(authToken); StephaneLenclud@233: } StephaneLenclud@256: StephaneLenclud@270: if (!Program.HarmonyClient.IsReady || !success StephaneLenclud@270: // Only first failure triggers new Harmony server AUTH StephaneLenclud@270: // That's to avoid calling upon Logitech servers too often StephaneLenclud@270: && iHarmonyReconnectTries == 0 ) StephaneLenclud@233: { StephaneLenclud@256: //We failed to connect using our token StephaneLenclud@256: //Delete it then StephaneLenclud@270: Trace.WriteLine("Harmony: Reseting authentication token!"); StephaneLenclud@266: Properties.Settings.Default.LogitechAuthToken = ""; StephaneLenclud@266: Properties.Settings.Default.Save(); StephaneLenclud@256: StephaneLenclud@253: Trace.WriteLine("Harmony: Authenticating with Logitech servers..."); St?phane@271: success = await Program.HarmonyClient.TryOpenAsync(); StephaneLenclud@266: //Persist our authentication token in our setting StephaneLenclud@270: if (success) StephaneLenclud@270: { StephaneLenclud@270: Trace.WriteLine("Harmony: Saving authentication token."); StephaneLenclud@270: Properties.Settings.Default.LogitechAuthToken = Program.HarmonyClient.Token; StephaneLenclud@270: Properties.Settings.Default.Save(); StephaneLenclud@270: } StephaneLenclud@233: } StephaneLenclud@270: StephaneLenclud@270: // I've seen this failing with "Policy lookup failed on server". StephaneLenclud@270: Program.HarmonyConfig = await Program.HarmonyClient.TryGetConfigAsync(); StephaneLenclud@270: if (Program.HarmonyConfig == null) StephaneLenclud@270: { StephaneLenclud@270: success = false; StephaneLenclud@270: } StephaneLenclud@270: else StephaneLenclud@270: { StephaneLenclud@270: // So we now have our Harmony Configuration StephaneLenclud@270: PopulateTreeViewHarmony(Program.HarmonyConfig); StephaneLenclud@270: // Make sure harmony command actions are showing device name instead of device id StephaneLenclud@270: PopulateTreeViewEvents(CurrentEarObject()); StephaneLenclud@270: } StephaneLenclud@270: StephaneLenclud@270: // TODO: Consider putting the retry logic one level higher in ResetHarmonyAsync StephaneLenclud@270: if (!success) StephaneLenclud@270: { StephaneLenclud@270: // See if we need to keep trying StephaneLenclud@270: if (iHarmonyReconnectTries < KHarmonyMaxReconnectTries) StephaneLenclud@270: { StephaneLenclud@270: iHarmonyReconnectTries++; StephaneLenclud@270: Trace.WriteLine("Harmony: Failed to connect, try again: " + iHarmonyReconnectTries); StephaneLenclud@270: await ConnectHarmonyAsync(); StephaneLenclud@270: } StephaneLenclud@270: else StephaneLenclud@270: { StephaneLenclud@270: Trace.WriteLine("Harmony: Failed to connect, giving up!"); StephaneLenclud@270: iHarmonyReconnectTries = 0; StephaneLenclud@270: // TODO: Could use a data member as timer rather than a new instance. StephaneLenclud@270: // Try that again in 5 minutes then. StephaneLenclud@270: // Using Windows Form timer to make sure we run in the UI thread. StephaneLenclud@270: System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); StephaneLenclud@270: timer.Tick += async delegate (object sender, EventArgs e) StephaneLenclud@270: { StephaneLenclud@270: // Stop our timer first as we won't need it anymore StephaneLenclud@270: (sender as System.Windows.Forms.Timer).Stop(); StephaneLenclud@270: // Then try to connect again StephaneLenclud@270: await ConnectHarmonyAsync(); StephaneLenclud@270: }; StephaneLenclud@270: timer.Interval = 300000; StephaneLenclud@270: timer.Start(); StephaneLenclud@270: } StephaneLenclud@270: } StephaneLenclud@270: else StephaneLenclud@270: { StephaneLenclud@270: // We are connected with a valid Harmony Configuration StephaneLenclud@270: // Reset our tries counter then StephaneLenclud@270: iHarmonyReconnectTries = 0; StephaneLenclud@270: } StephaneLenclud@233: } StephaneLenclud@233: StephaneLenclud@233: /// StephaneLenclud@233: /// StephaneLenclud@233: /// StephaneLenclud@233: /// StephaneLenclud@236: private void PopulateTreeViewHarmony(HarmonyHub.Config aConfig) StephaneLenclud@233: { StephaneLenclud@233: iTreeViewHarmony.Nodes.Clear(); StephaneLenclud@233: //Add our devices StephaneLenclud@236: foreach (HarmonyHub.Device device in aConfig.Devices) StephaneLenclud@233: { StephaneLenclud@233: TreeNode deviceNode = iTreeViewHarmony.Nodes.Add(device.Id, $"{device.Label} ({device.DeviceTypeDisplayName}/{device.Model})"); StephaneLenclud@233: deviceNode.Tag = device; StephaneLenclud@233: StephaneLenclud@236: foreach (HarmonyHub.ControlGroup cg in device.ControlGroups) StephaneLenclud@233: { StephaneLenclud@233: TreeNode cgNode = deviceNode.Nodes.Add(cg.Name); StephaneLenclud@233: cgNode.Tag = cg; StephaneLenclud@233: StephaneLenclud@236: foreach (HarmonyHub.Function f in cg.Functions) StephaneLenclud@233: { StephaneLenclud@257: TreeNode fNode = cgNode.Nodes.Add(f.Label); StephaneLenclud@233: fNode.Tag = f; StephaneLenclud@233: } StephaneLenclud@233: } StephaneLenclud@233: } StephaneLenclud@233: StephaneLenclud@233: //treeViewConfig.ExpandAll(); StephaneLenclud@233: } StephaneLenclud@233: StephaneLenclud@233: private async void iTreeViewHarmony_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) StephaneLenclud@233: { StephaneLenclud@233: //Upon function node double click we execute it StephaneLenclud@236: var tag = e.Node.Tag as HarmonyHub.Function; StephaneLenclud@236: if (tag != null && e.Node.Parent.Parent.Tag is HarmonyHub.Device) StephaneLenclud@233: { StephaneLenclud@236: HarmonyHub.Function f = tag; StephaneLenclud@236: HarmonyHub.Device d = (HarmonyHub.Device)e.Node.Parent.Parent.Tag; StephaneLenclud@233: StephaneLenclud@257: Trace.WriteLine($"Harmony: Sending {f.Label} to {d.Label}..."); StephaneLenclud@257: StephaneLenclud@257: await Program.HarmonyClient.TrySendKeyPressAsync(d.Id, f.Action.Command); StephaneLenclud@233: } StephaneLenclud@233: } Stephane@252: sl@0: } sl@0: }