Imported Upstream version 2.2.0
[deb_libcec.git] / src / LibCecTray / controller / applications / ApplicationController.cs
CommitLineData
cbbe90dd
JB
1/*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011-2013 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{
45 public delegate void OnApplicationRunningChanged(bool running);
46
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(CECController controller, string uiName, string processName, string filename, string workingDirectory)
53 {
54 Controller = controller;
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(CECController controller, 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(controller, 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 if (args.ColumnIndex >= 0)
118 {
119 (new CecButtonConfigUI(item)).ShowDialog();
120 }
121 else
122 {
123 var mappedButton = ButtonConfig[item.Key];
124 if (mappedButton == null || mappedButton.Value.Empty())
125 return;
126
127 var controlWindow = FindInstance();
128 if (controlWindow != IntPtr.Zero && item.Key.Duration == 0)
129 mappedButton.Value.Transmit(controlWindow);
130 }
131 };
132
133 foreach (var item in _buttonConfig)
134 {
135 item.SettingChanged += delegate
136 {
137 gridView.Refresh();
138 };
139 }
140 }
141
142 #region Start and stop the application
143 /// <summary>
144 /// Check if the application is running
145 /// </summary>
146 /// <returns>True when running, false otherwise</returns>
147 public virtual bool IsRunning()
148 {
149 return FindInstance() != IntPtr.Zero;
150 }
151
152 /// <summary>
153 /// Start the application if it's not running already, and suppress further starts for 5 seconds
154 /// </summary>
155 /// <returns>True when started or suppressed, false otherwise</returns>
156 public virtual bool Start(bool bExitAfterStarting)
157 {
158 if (IsRunning())
159 {
160 SetForeground();
161 return true;
162 }
163
164 if (SuppressApplicationStart)
165 return false;
166
167 SuppressApplicationStart = true;
168 Timer timer = new Timer {Interval = 5000, AutoReset = false};
169 timer.Elapsed += delegate { SuppressApplicationStart = false; };
170 timer.Start();
171
172 try
173 {
174 using (
175 Process runningProcess = new Process
176 {
177 StartInfo =
178 {
179 WorkingDirectory = ApplicationWorkingDirectory,
180 FileName = ApplicationFilename
181 }
182 })
183 {
184 // start maximised if the option is enabled
185 if (StartFullScreen.Value)
186 runningProcess.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
187
188 runningProcess.Start();
189 }
190 }
191 catch (Exception)
192 {
193 return false;
194 }
195
196 if (bExitAfterStarting)
197 Application.Exit();
198
199 return true;
200 }
201
202 /// <summary>
203 /// Initialise the controller and autostart the application
204 /// </summary>
205 public virtual void Initialise()
206 {
207 Timer timer = new Timer { Interval = 1000, AutoReset = true };
208 timer.Elapsed += delegate { CheckApplicationEnabled(); };
209 timer.Start();
210
211 if (AutoStartApplication.Value)
212 Start(false);
213 }
214
215 public event OnApplicationRunningChanged ApplicationRunningChanged;
216
217 private void CheckApplicationEnabled()
218 {
219 var isRunning = IsRunning();
220 if (isRunning != _applicationRunning && ApplicationRunningChanged != null)
221 ApplicationRunningChanged(isRunning);
222
223 _applicationRunning = isRunning;
224 UiControl.SetStartButtonEnabled(!isRunning && !SuppressApplicationStart);
225 }
226 #endregion
227
228 #region Send input to the application
229 /// <summary>
230 /// Send a keypress to the application if it's running
231 /// </summary>
232 /// <param name="key">The keypress to send</param>
233 /// <param name="isSelectedTab">True when this tab is currently selected in the UI</param>
234 /// <returns>True when sent, false otherwise</returns>
235 public virtual bool SendKey(CecKeypress key, bool isSelectedTab)
236 {
237 if (isSelectedTab)
238 UiControl.SelectKeypressRow(UiControl, CecButtonGridView, key);
239
240 if (isSelectedTab && SuppressKeypressWhenSelected.Value)
241 return false;
242
243 if (!ControlApplication.Value)
244 return false;
245
246 var mappedButton = ButtonConfig[key];
247 if (mappedButton == null || mappedButton.Value.Empty())
248 return false;
249
250 var controlWindow = FindInstance();
251 if (controlWindow != IntPtr.Zero && (key.Duration == 0 || key.Duration > 500))
252 return mappedButton.Value.Transmit(controlWindow);
253
254 return false;
255 }
256 #endregion
257
258 #region Process control
259 /// <summary>
260 /// Make this application the foreground application if it's running
261 /// </summary>
262 public virtual void SetForeground()
263 {
264 var wmcInstance = FindInstance();
265 if (wmcInstance != IntPtr.Zero)
266 WindowsAPI.SetForegroundWindow(wmcInstance);
267 }
268
269 /// <summary>
270 /// The main window handle of the application if it's running.
271 /// </summary>
272 /// <returns>The main window handle, or IntPtr.Zero if it's not found</returns>
273 protected virtual IntPtr FindInstance()
274 {
275 var processes = Process.GetProcessesByName(ProcessName);
276 foreach (var process in processes)
277 {
278 if (process.MainWindowHandle != IntPtr.Zero)
279 return process.MainWindowHandle;
280 }
281 return IntPtr.Zero;
282 }
283 #endregion
284
285 #region Members
286 /// <summary>
287 /// The name of the process in the process manager
288 /// </summary>
289 public string ProcessName { set; get; }
290
291 /// <summary>
292 /// The filename of the application
293 /// </summary>
294 public string ApplicationFilename { set; get; }
295
296 /// <summary>
297 /// The working directory of the application
298 /// </summary>
299 public string ApplicationWorkingDirectory { set; get; }
300
301 /// <summary>
302 /// Don't start the application while true
303 /// </summary>
304 public bool SuppressApplicationStart { get; private set; }
305
306 /// <summary>
307 /// The name of the application how it shows up in this application
308 /// </summary>
309 public string UiName { set; get; }
310
311 /// <summary>
312 /// True when this application should be autostarted when this application is activated, or made the active source
313 /// </summary>
314 public CECSettingBool AutoStartApplication
315 {
316 get
317 {
318 if (!Settings.ContainsKey(ProcessName + "_autostart"))
319 {
320 CECSettingBool setting = new CECSettingBool(ProcessName + "_autostart", "Autostart application", false, null);
321 Settings.Load(setting);
322 Settings[ProcessName + "_autostart"] = setting;
323 }
324 return Settings[ProcessName + "_autostart"].AsSettingBool;
325 }
326 }
327
328 /// <summary>
329 /// True when keypresses should be routed to this application
330 /// </summary>
331 public CECSettingBool ControlApplication
332 {
333 get
334 {
335 if (!Settings.ContainsKey(ProcessName + "_control"))
336 {
337 CECSettingBool setting = new CECSettingBool(ProcessName + "_control", "Control application", true, null);
338 Settings.Load(setting);
339 Settings[ProcessName + "_control"] = setting;
340 }
341 return Settings[ProcessName + "_control"].AsSettingBool;
342 }
343 }
344
345 /// <summary>
346 /// True when this application should be autostarted when this application is activated, or made the active source
347 /// </summary>
348 public CECSettingBool SuppressKeypressWhenSelected
349 {
350 get
351 {
352 if (!Settings.ContainsKey(ProcessName + "_suppress_when_selected"))
353 {
354 CECSettingBool setting = new CECSettingBool(ProcessName + "_suppress_when_selected", "Suppress keypress when this tab is selected", true, null);
355 Settings.Load(setting);
356 Settings[ProcessName + "_suppress_when_selected"] = setting;
357 }
358 return Settings[ProcessName + "_suppress_when_selected"].AsSettingBool;
359 }
360 }
361
362 /// <summary>
363 /// True when the application should be started in full screen mode
364 /// </summary>
365 public CECSettingBool StartFullScreen
366 {
367 get
368 {
369 if (!Settings.ContainsKey(ProcessName + "_start_fullscreen"))
370 {
371 CECSettingBool setting = new CECSettingBool(ProcessName + "_start_fullscreen", "Start in full screen mode", true, null);
372 Settings.Load(setting);
373 Settings[ProcessName + "_start_fullscreen"] = setting;
374 }
375 return Settings[ProcessName + "_start_fullscreen"].AsSettingBool;
376 }
377 }
378
379 protected ControllerTabPage UIControlInternal;
380 public virtual ControllerTabPage UiControl
381 {
382 get { return UIControlInternal ?? (UIControlInternal = new ApplicationControllerUI(this)); }
383 }
384
385 private CecButtonConfig _buttonConfig;
386 public CecButtonConfig ButtonConfig
387 {
388 get { return _buttonConfig ?? (_buttonConfig = new CecButtonConfig(this)); }
389 }
390
391 public CECSettings Settings
392 {
393 get { return Controller.Settings; }
394 }
395 protected DataGridView CecButtonGridView;
396
397 public virtual ApplicationAction DefaultValue(CecKeypress key)
398 {
399 return null;
400 }
401
402 public virtual bool HasDefaultValue(CecKeypress key)
403 {
404 return DefaultValue(key) != null;
405 }
406
407 public bool IsInternal { protected set; get; }
408 public bool CanConfigureProcess
409 {
410 get
411 {
412 return !IsInternal;
413 }
414 }
415
416 private bool _applicationRunning;
417
418 protected readonly CECController Controller;
419
420 #endregion
421 }
422}