/* * This file is part of the libCEC(R) library. * * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved. * libCEC(R) is an original work, containing original code. * * libCEC(R) is a trademark of Pulse-Eight Limited. * * This program is dual-licensed; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * Alternatively, you can license this library under a commercial license, * please contact Pulse-Eight Licensing for more information. * * For more information contact: * Pulse-Eight Licensing * http://www.pulse-eight.com/ * http://www.pulse-eight.net/ */ using System; using System.Diagnostics; using System.Drawing; using System.IO; using System.Windows.Forms; using CecSharp; using LibCECTray.Properties; using LibCECTray.settings; using Timer = System.Timers.Timer; namespace LibCECTray.controller.applications { public delegate void OnApplicationRunningChanged(bool running); /// /// Controls an application on the PC: send key presses, open the application, close it, etc. /// class ApplicationController { public ApplicationController(CECController controller, string uiName, string processName, string filename, string workingDirectory) { Controller = controller; UiName = uiName; ProcessName = processName; ApplicationFilename = filename; ApplicationWorkingDirectory = workingDirectory; SuppressApplicationStart = false; IsInternal = false; } public static ApplicationController FromString(CECController controller, CECSettings settings, string serialisedConfig) { var splitString = serialisedConfig.Split(';'); if (splitString.Length != 4) throw new InvalidDataException("incorrect number of parameters"); return new ApplicationController(controller, splitString[0], splitString[1], splitString[2], splitString[3]); } public string AsString() { return string.Format("{0};{1};{2};{3}", UiName, ProcessName, ApplicationFilename, ApplicationWorkingDirectory); } public void BindButtonConfiguration(DataGridView gridView, BindingSource bindingSource) { CecButtonGridView = gridView; DataGridViewCell buttonCellTemplate = new DataGridViewTextBoxCell(); CecButtonGridView.Columns.Add(new DataGridViewColumn(buttonCellTemplate) { DataPropertyName = "CecButtonName", Name = Resources.config_cec_button, ReadOnly = true, Width = 150 }); DataGridViewButtonCell mappedToCellTemplate = new DataGridViewButtonCell(); CecButtonGridView.Columns.Add(new DataGridViewColumn(mappedToCellTemplate) { DataPropertyName = "MappedButtonName", Name = Resources.config_button_mapped_to, ReadOnly = true, Width = 350 }); bindingSource.DataSource = ButtonConfig; CecButtonGridView.DataSource = bindingSource; gridView.CellFormatting += delegate(object sender, DataGridViewCellFormattingEventArgs args) { DataGridView grid = sender as DataGridView; var data = grid != null ? grid.Rows[args.RowIndex].DataBoundItem as CecButtonConfigItem : null; if (data == null || !data.Enabled) { args.CellStyle.ForeColor = Color.Gray; } }; gridView.CellClick += delegate(object sender, DataGridViewCellEventArgs args) { var item = args.RowIndex < ButtonConfig.Count ? ButtonConfig[args.RowIndex] : null; if (item == null) return; if (args.ColumnIndex >= 0) { (new CecButtonConfigUI(item)).ShowDialog(); } else { var mappedButton = ButtonConfig[item.Key]; if (mappedButton == null || mappedButton.Value.Empty()) return; var controlWindow = FindInstance(); if (controlWindow != IntPtr.Zero && item.Key.Duration == 0) mappedButton.Value.Transmit(controlWindow); } }; foreach (var item in _buttonConfig) { item.SettingChanged += delegate { gridView.Refresh(); }; } } #region Start and stop the application /// /// Check if the application is running /// /// True when running, false otherwise public virtual bool IsRunning() { return FindInstance() != IntPtr.Zero; } /// /// Start the application if it's not running already, and suppress further starts for 5 seconds /// /// True when started or suppressed, false otherwise public virtual bool Start(bool bExitAfterStarting) { if (IsRunning()) { SetForeground(); return true; } if (SuppressApplicationStart) return false; SuppressApplicationStart = true; Timer timer = new Timer {Interval = 5000, AutoReset = false}; timer.Elapsed += delegate { SuppressApplicationStart = false; }; timer.Start(); try { using ( Process runningProcess = new Process { StartInfo = { WorkingDirectory = ApplicationWorkingDirectory, FileName = ApplicationFilename } }) { // start maximised if the option is enabled if (StartFullScreen.Value) runningProcess.StartInfo.WindowStyle = ProcessWindowStyle.Maximized; runningProcess.Start(); } } catch (Exception) { return false; } if (bExitAfterStarting) Application.Exit(); return true; } /// /// Initialise the controller and autostart the application /// public virtual void Initialise() { Timer timer = new Timer { Interval = 1000, AutoReset = true }; timer.Elapsed += delegate { CheckApplicationEnabled(); }; timer.Start(); if (AutoStartApplication.Value) Start(false); } public event OnApplicationRunningChanged ApplicationRunningChanged; private void CheckApplicationEnabled() { var isRunning = IsRunning(); if (isRunning != _applicationRunning && ApplicationRunningChanged != null) ApplicationRunningChanged(isRunning); _applicationRunning = isRunning; UiControl.SetStartButtonEnabled(!isRunning && !SuppressApplicationStart); } #endregion #region Send input to the application /// /// Send a keypress to the application if it's running /// /// The keypress to send /// True when this tab is currently selected in the UI /// True when sent, false otherwise public virtual bool SendKey(CecKeypress key, bool isSelectedTab) { if (isSelectedTab) UiControl.SelectKeypressRow(UiControl, CecButtonGridView, key); if (isSelectedTab && SuppressKeypressWhenSelected.Value) return false; if (!ControlApplication.Value) return false; var mappedButton = ButtonConfig[key]; if (mappedButton == null || mappedButton.Value.Empty()) return false; var controlWindow = FindInstance(); if (controlWindow != IntPtr.Zero && (key.Duration == 0 || key.Duration > 500)) return mappedButton.Value.Transmit(controlWindow); return false; } #endregion #region Process control /// /// Make this application the foreground application if it's running /// public virtual void SetForeground() { var wmcInstance = FindInstance(); if (wmcInstance != IntPtr.Zero) WindowsAPI.SetForegroundWindow(wmcInstance); } /// /// The main window handle of the application if it's running. /// /// The main window handle, or IntPtr.Zero if it's not found protected virtual IntPtr FindInstance() { var processes = Process.GetProcessesByName(ProcessName); foreach (var process in processes) { if (process.MainWindowHandle != IntPtr.Zero) return process.MainWindowHandle; } return IntPtr.Zero; } #endregion #region Members /// /// The name of the process in the process manager /// public string ProcessName { set; get; } /// /// The filename of the application /// public string ApplicationFilename { set; get; } /// /// The working directory of the application /// public string ApplicationWorkingDirectory { set; get; } /// /// Don't start the application while true /// public bool SuppressApplicationStart { get; private set; } /// /// The name of the application how it shows up in this application /// public string UiName { set; get; } /// /// True when this application should be autostarted when this application is activated, or made the active source /// public CECSettingBool AutoStartApplication { get { if (!Settings.ContainsKey(ProcessName + "_autostart")) { CECSettingBool setting = new CECSettingBool(ProcessName + "_autostart", "Autostart application", false, null); Settings.Load(setting); Settings[ProcessName + "_autostart"] = setting; } return Settings[ProcessName + "_autostart"].AsSettingBool; } } /// /// True when keypresses should be routed to this application /// public CECSettingBool ControlApplication { get { if (!Settings.ContainsKey(ProcessName + "_control")) { CECSettingBool setting = new CECSettingBool(ProcessName + "_control", "Control application", true, null); Settings.Load(setting); Settings[ProcessName + "_control"] = setting; } return Settings[ProcessName + "_control"].AsSettingBool; } } /// /// True when this application should be autostarted when this application is activated, or made the active source /// public CECSettingBool SuppressKeypressWhenSelected { get { if (!Settings.ContainsKey(ProcessName + "_suppress_when_selected")) { CECSettingBool setting = new CECSettingBool(ProcessName + "_suppress_when_selected", "Suppress keypress when this tab is selected", true, null); Settings.Load(setting); Settings[ProcessName + "_suppress_when_selected"] = setting; } return Settings[ProcessName + "_suppress_when_selected"].AsSettingBool; } } /// /// True when the application should be started in full screen mode /// public CECSettingBool StartFullScreen { get { if (!Settings.ContainsKey(ProcessName + "_start_fullscreen")) { CECSettingBool setting = new CECSettingBool(ProcessName + "_start_fullscreen", "Start in full screen mode", true, null); Settings.Load(setting); Settings[ProcessName + "_start_fullscreen"] = setting; } return Settings[ProcessName + "_start_fullscreen"].AsSettingBool; } } protected ControllerTabPage UIControlInternal; public virtual ControllerTabPage UiControl { get { return UIControlInternal ?? (UIControlInternal = new ApplicationControllerUI(this)); } } private CecButtonConfig _buttonConfig; public CecButtonConfig ButtonConfig { get { return _buttonConfig ?? (_buttonConfig = new CecButtonConfig(this)); } } public CECSettings Settings { get { return Controller.Settings; } } protected DataGridView CecButtonGridView; public virtual ApplicationAction DefaultValue(CecKeypress key) { return null; } public virtual bool HasDefaultValue(CecKeypress key) { return DefaultValue(key) != null; } public bool IsInternal { protected set; get; } public bool CanConfigureProcess { get { return !IsInternal; } } private bool _applicationRunning; protected readonly CECController Controller; #endregion } }