Merge pull request #112 from linuxstb/master
[deb_libcec.git] / src / LibCecTray / controller / applications / ApplicationController.cs
CommitLineData
f017f3c4
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
5 * libCEC(R) is an original work, containing original code.
6 *
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
8 *
9 * This program is dual-licensed; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 *
23 *
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
26 *
27 * For more information contact:
28 * Pulse-Eight Licensing <license@pulse-eight.com>
29 * http://www.pulse-eight.com/
30 * http://www.pulse-eight.net/
31 */
32
33using System;
34using System.Diagnostics;
35using System.Drawing;
36using System.IO;
37using System.Windows.Forms;
38using CecSharp;
39using LibCECTray.Properties;
40using LibCECTray.settings;
41using Timer = System.Timers.Timer;
42
43namespace LibCECTray.controller.applications
44{
f4729954
LOK
45 public delegate void OnApplicationRunningChanged(bool running);
46
f017f3c4
LOK
47 /// <summary>
48 /// Controls an application on the PC: send key presses, open the application, close it, etc.
49 /// </summary>
50 class ApplicationController
51 {
52 public ApplicationController(CECSettings settings, string uiName, string processName, string filename, string workingDirectory)
53 {
54 Settings = settings;
55 UiName = uiName;
56 ProcessName = processName;
57 ApplicationFilename = filename;
58 ApplicationWorkingDirectory = workingDirectory;
59 SuppressApplicationStart = false;
60 IsInternal = false;
61 }
62
63 public static ApplicationController FromString(CECSettings settings, string serialisedConfig)
64 {
65 var splitString = serialisedConfig.Split(';');
66 if (splitString.Length != 4)
67 throw new InvalidDataException("incorrect number of parameters");
68
69 return new ApplicationController(settings, splitString[0], splitString[1], splitString[2], splitString[3]);
70 }
71
72 public string AsString()
73 {
74 return string.Format("{0};{1};{2};{3}", UiName, ProcessName, ApplicationFilename, ApplicationWorkingDirectory);
75 }
76
77 public void BindButtonConfiguration(DataGridView gridView, BindingSource bindingSource)
78 {
79 CecButtonGridView = gridView;
80
81 DataGridViewCell buttonCellTemplate = new DataGridViewTextBoxCell();
82 CecButtonGridView.Columns.Add(new DataGridViewColumn(buttonCellTemplate)
83 {
84 DataPropertyName = "CecButtonName",
85 Name = Resources.config_cec_button,
86 ReadOnly = true,
87 Width = 150
88 });
89
90 DataGridViewButtonCell mappedToCellTemplate = new DataGridViewButtonCell();
91 CecButtonGridView.Columns.Add(new DataGridViewColumn(mappedToCellTemplate)
92 {
93 DataPropertyName = "MappedButtonName",
94 Name = Resources.config_button_mapped_to,
95 ReadOnly = true,
96 Width = 350
97 });
98
99 bindingSource.DataSource = ButtonConfig;
100 CecButtonGridView.DataSource = bindingSource;
101
102 gridView.CellFormatting += delegate(object sender, DataGridViewCellFormattingEventArgs args)
103 {
104 DataGridView grid = sender as DataGridView;
105 var data = grid != null ? grid.Rows[args.RowIndex].DataBoundItem as CecButtonConfigItem : null;
106 if (data == null || !data.Enabled)
107 {
108 args.CellStyle.ForeColor = Color.Gray;
109 }
110 };
111
112 gridView.CellClick += delegate(object sender, DataGridViewCellEventArgs args)
113 {
114 var item = args.RowIndex < ButtonConfig.Count ? ButtonConfig[args.RowIndex] : null;
115 if (item == null)
116 return;
117 (new CecButtonConfigUI(item)).ShowDialog();
118 };
119
120 foreach (var item in _buttonConfig)
121 {
122 item.SettingChanged += delegate
123 {
124 gridView.Refresh();
125 };
126 }
127 }
128
129 #region Start and stop the application
130 /// <summary>
131 /// Check if the application is running
132 /// </summary>
133 /// <returns>True when running, false otherwise</returns>
134 public virtual bool IsRunning()
135 {
136 return FindInstance() != IntPtr.Zero;
137 }
138
139 /// <summary>
140 /// Start the application if it's not running already, and suppress further starts for 5 seconds
141 /// </summary>
142 /// <returns>True when started or suppressed, false otherwise</returns>
143 public virtual bool Start(bool bExitAfterStarting)
144 {
145 if (IsRunning())
146 {
147 SetForeground();
148 return true;
149 }
150
151 if (SuppressApplicationStart)
152 return false;
153
154 SuppressApplicationStart = true;
155 Timer timer = new Timer {Interval = 5000, AutoReset = false};
156 timer.Elapsed += delegate { SuppressApplicationStart = false; };
157 timer.Start();
158
159 try
160 {
161 using (
162 Process runningProcess = new Process
163 {
164 StartInfo =
165 {
166 WorkingDirectory = ApplicationWorkingDirectory,
167 FileName = ApplicationFilename
168 }
169 })
170 {
171 // start maximised if the option is enabled
172 if (StartFullScreen.Value)
173 runningProcess.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
174
175 runningProcess.Start();
176 }
177 }
178 catch (Exception)
179 {
180 return false;
181 }
182
183 if (bExitAfterStarting)
184 Application.Exit();
185
186 return true;
187 }
188
189 /// <summary>
190 /// Initialise the controller and autostart the application
191 /// </summary>
192 public virtual void Initialise()
193 {
194 Timer timer = new Timer { Interval = 1000, AutoReset = true };
f4729954 195 timer.Elapsed += delegate { CheckApplicationEnabled(); };
f017f3c4
LOK
196 timer.Start();
197
198 if (AutoStartApplication.Value)
199 Start(false);
200 }
f4729954
LOK
201
202 public event OnApplicationRunningChanged ApplicationRunningChanged;
203
204 private void CheckApplicationEnabled()
205 {
206 var isRunning = IsRunning();
207 if (isRunning != _applicationRunning && ApplicationRunningChanged != null)
208 ApplicationRunningChanged(isRunning);
209
210 _applicationRunning = isRunning;
3d15aebf 211 UiControl.SetStartButtonEnabled(!isRunning && !SuppressApplicationStart);
f4729954 212 }
f017f3c4
LOK
213 #endregion
214
215 #region Send input to the application
216 /// <summary>
217 /// Send a keypress to the application if it's running
218 /// </summary>
219 /// <param name="key">The keypress to send</param>
220 /// <param name="isSelectedTab">True when this tab is currently selected in the UI</param>
221 /// <returns>True when sent, false otherwise</returns>
222 public virtual bool SendKey(CecKeypress key, bool isSelectedTab)
223 {
224 if (isSelectedTab)
225 UiControl.SelectKeypressRow(UiControl, CecButtonGridView, key);
226
227 if (isSelectedTab && SuppressKeypressWhenSelected.Value)
228 return false;
229
230 if (!ControlApplication.Value)
231 return false;
232
233 var mappedButton = ButtonConfig[key];
234 if (mappedButton == null || mappedButton.Value.Empty())
235 return false;
236
237 var controlWindow = FindInstance();
238 if (controlWindow != IntPtr.Zero && key.Duration == 0)
239 return mappedButton.Value.Transmit(controlWindow);
240
241 return false;
242 }
243 #endregion
244
245 #region Process control
246 /// <summary>
247 /// Make this application the foreground application if it's running
248 /// </summary>
249 public virtual void SetForeground()
250 {
251 var wmcInstance = FindInstance();
252 if (wmcInstance != IntPtr.Zero)
253 WindowsAPI.SetForegroundWindow(wmcInstance);
254 }
255
256 /// <summary>
257 /// The main window handle of the application if it's running.
258 /// </summary>
259 /// <returns>The main window handle, or IntPtr.Zero if it's not found</returns>
260 protected virtual IntPtr FindInstance()
261 {
262 var processes = Process.GetProcessesByName(ProcessName);
263 return processes.Length > 0 ? processes[0].MainWindowHandle : IntPtr.Zero;
264 }
265 #endregion
266
267 #region Members
268 /// <summary>
269 /// The name of the process in the process manager
270 /// </summary>
271 public string ProcessName { set; get; }
272
273 /// <summary>
274 /// The filename of the application
275 /// </summary>
276 public string ApplicationFilename { set; get; }
277
278 /// <summary>
279 /// The working directory of the application
280 /// </summary>
281 public string ApplicationWorkingDirectory { set; get; }
282
283 /// <summary>
284 /// Don't start the application while true
285 /// </summary>
286 public bool SuppressApplicationStart { get; private set; }
287
288 /// <summary>
289 /// The name of the application how it shows up in this application
290 /// </summary>
291 public string UiName { set; get; }
292
293 /// <summary>
294 /// True when this application should be autostarted when this application is activated, or made the active source
295 /// </summary>
296 public CECSettingBool AutoStartApplication
297 {
298 get
299 {
300 if (!Settings.ContainsKey(ProcessName + "_autostart"))
301 {
cf3303fc 302 CECSettingBool setting = new CECSettingBool(ProcessName + "_autostart", "Autostart application", false, null);
f017f3c4
LOK
303 Settings.Load(setting);
304 Settings[ProcessName + "_autostart"] = setting;
305 }
306 return Settings[ProcessName + "_autostart"].AsSettingBool;
307 }
308 }
309
310 /// <summary>
311 /// True when keypresses should be routed to this application
312 /// </summary>
313 public CECSettingBool ControlApplication
314 {
315 get
316 {
317 if (!Settings.ContainsKey(ProcessName + "_control"))
318 {
319 CECSettingBool setting = new CECSettingBool(ProcessName + "_control", "Control application", true, null);
320 Settings.Load(setting);
321 Settings[ProcessName + "_control"] = setting;
322 }
323 return Settings[ProcessName + "_control"].AsSettingBool;
324 }
325 }
326
327 /// <summary>
328 /// True when this application should be autostarted when this application is activated, or made the active source
329 /// </summary>
330 public CECSettingBool SuppressKeypressWhenSelected
331 {
332 get
333 {
334 if (!Settings.ContainsKey(ProcessName + "_suppress_when_selected"))
335 {
336 CECSettingBool setting = new CECSettingBool(ProcessName + "_suppress_when_selected", "Suppress keypress when this tab is selected", true, null);
337 Settings.Load(setting);
338 Settings[ProcessName + "_suppress_when_selected"] = setting;
339 }
340 return Settings[ProcessName + "_suppress_when_selected"].AsSettingBool;
341 }
342 }
343
344 /// <summary>
345 /// True when the application should be started in full screen mode
346 /// </summary>
347 public CECSettingBool StartFullScreen
348 {
349 get
350 {
351 if (!Settings.ContainsKey(ProcessName + "_start_fullscreen"))
352 {
353 CECSettingBool setting = new CECSettingBool(ProcessName + "_start_fullscreen", "Start in full screen mode", true, null);
354 Settings.Load(setting);
355 Settings[ProcessName + "_start_fullscreen"] = setting;
356 }
357 return Settings[ProcessName + "_start_fullscreen"].AsSettingBool;
358 }
359 }
360
361 protected ControllerTabPage UIControlInternal;
362 public virtual ControllerTabPage UiControl
363 {
364 get { return UIControlInternal ?? (UIControlInternal = new ApplicationControllerUI(this)); }
365 }
366
367 private CecButtonConfig _buttonConfig;
368 public CecButtonConfig ButtonConfig
369 {
324a4cb1 370 get { return _buttonConfig ?? (_buttonConfig = new CecButtonConfig(this)); }
f017f3c4
LOK
371 }
372
373 public CECSettings Settings;
374 protected DataGridView CecButtonGridView;
375
376 public virtual ApplicationAction DefaultValue(CecKeypress key)
377 {
378 return null;
379 }
380
381 public virtual bool HasDefaultValue(CecKeypress key)
382 {
383 return DefaultValue(key) != null;
384 }
385
386 public bool IsInternal { protected set; get; }
387 public bool CanConfigureProcess
388 {
389 get
390 {
391 return !IsInternal;
392 }
393 }
f4729954
LOK
394
395 private bool _applicationRunning;
396
f017f3c4
LOK
397 #endregion
398 }
399}