Commit | Line | Data |
---|---|---|
f017f3c4 LOK |
1 | /* |
2 | * This file is part of the libCEC(R) library. | |
3 | * | |
16f47961 | 4 | * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved. |
f017f3c4 LOK |
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 | ||
33 | using System; | |
34 | using System.Diagnostics; | |
35 | using System.Drawing; | |
36 | using System.IO; | |
37 | using System.Windows.Forms; | |
38 | using CecSharp; | |
39 | using LibCECTray.Properties; | |
40 | using LibCECTray.settings; | |
41 | using Timer = System.Timers.Timer; | |
42 | ||
43 | namespace 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 | { | |
97401db1 | 52 | public ApplicationController(CECController controller, string uiName, string processName, string filename, string workingDirectory) |
f017f3c4 | 53 | { |
97401db1 | 54 | Controller = controller; |
f017f3c4 LOK |
55 | UiName = uiName; |
56 | ProcessName = processName; | |
57 | ApplicationFilename = filename; | |
58 | ApplicationWorkingDirectory = workingDirectory; | |
59 | SuppressApplicationStart = false; | |
60 | IsInternal = false; | |
61 | } | |
62 | ||
97401db1 | 63 | public static ApplicationController FromString(CECController controller, CECSettings settings, string serialisedConfig) |
f017f3c4 LOK |
64 | { |
65 | var splitString = serialisedConfig.Split(';'); | |
66 | if (splitString.Length != 4) | |
67 | throw new InvalidDataException("incorrect number of parameters"); | |
68 | ||
97401db1 | 69 | return new ApplicationController(controller, splitString[0], splitString[1], splitString[2], splitString[3]); |
f017f3c4 LOK |
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; | |
92e1fc9c NB |
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 | } | |
f017f3c4 LOK |
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 }; | |
f4729954 | 208 | timer.Elapsed += delegate { CheckApplicationEnabled(); }; |
f017f3c4 LOK |
209 | timer.Start(); |
210 | ||
211 | if (AutoStartApplication.Value) | |
212 | Start(false); | |
213 | } | |
f4729954 LOK |
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; | |
3d15aebf | 224 | UiControl.SetStartButtonEnabled(!isRunning && !SuppressApplicationStart); |
f4729954 | 225 | } |
f017f3c4 LOK |
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(); | |
92e1fc9c | 251 | if (controlWindow != IntPtr.Zero && (key.Duration == 0 || key.Duration > 500)) |
f017f3c4 LOK |
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 | return processes.Length > 0 ? processes[0].MainWindowHandle : IntPtr.Zero; | |
277 | } | |
278 | #endregion | |
279 | ||
280 | #region Members | |
281 | /// <summary> | |
282 | /// The name of the process in the process manager | |
283 | /// </summary> | |
284 | public string ProcessName { set; get; } | |
285 | ||
286 | /// <summary> | |
287 | /// The filename of the application | |
288 | /// </summary> | |
289 | public string ApplicationFilename { set; get; } | |
290 | ||
291 | /// <summary> | |
292 | /// The working directory of the application | |
293 | /// </summary> | |
294 | public string ApplicationWorkingDirectory { set; get; } | |
295 | ||
296 | /// <summary> | |
297 | /// Don't start the application while true | |
298 | /// </summary> | |
299 | public bool SuppressApplicationStart { get; private set; } | |
300 | ||
301 | /// <summary> | |
302 | /// The name of the application how it shows up in this application | |
303 | /// </summary> | |
304 | public string UiName { set; get; } | |
305 | ||
306 | /// <summary> | |
307 | /// True when this application should be autostarted when this application is activated, or made the active source | |
308 | /// </summary> | |
309 | public CECSettingBool AutoStartApplication | |
310 | { | |
311 | get | |
312 | { | |
313 | if (!Settings.ContainsKey(ProcessName + "_autostart")) | |
314 | { | |
cf3303fc | 315 | CECSettingBool setting = new CECSettingBool(ProcessName + "_autostart", "Autostart application", false, null); |
f017f3c4 LOK |
316 | Settings.Load(setting); |
317 | Settings[ProcessName + "_autostart"] = setting; | |
318 | } | |
319 | return Settings[ProcessName + "_autostart"].AsSettingBool; | |
320 | } | |
321 | } | |
322 | ||
323 | /// <summary> | |
324 | /// True when keypresses should be routed to this application | |
325 | /// </summary> | |
326 | public CECSettingBool ControlApplication | |
327 | { | |
328 | get | |
329 | { | |
330 | if (!Settings.ContainsKey(ProcessName + "_control")) | |
331 | { | |
332 | CECSettingBool setting = new CECSettingBool(ProcessName + "_control", "Control application", true, null); | |
333 | Settings.Load(setting); | |
334 | Settings[ProcessName + "_control"] = setting; | |
335 | } | |
336 | return Settings[ProcessName + "_control"].AsSettingBool; | |
337 | } | |
338 | } | |
339 | ||
340 | /// <summary> | |
341 | /// True when this application should be autostarted when this application is activated, or made the active source | |
342 | /// </summary> | |
343 | public CECSettingBool SuppressKeypressWhenSelected | |
344 | { | |
345 | get | |
346 | { | |
347 | if (!Settings.ContainsKey(ProcessName + "_suppress_when_selected")) | |
348 | { | |
349 | CECSettingBool setting = new CECSettingBool(ProcessName + "_suppress_when_selected", "Suppress keypress when this tab is selected", true, null); | |
350 | Settings.Load(setting); | |
351 | Settings[ProcessName + "_suppress_when_selected"] = setting; | |
352 | } | |
353 | return Settings[ProcessName + "_suppress_when_selected"].AsSettingBool; | |
354 | } | |
355 | } | |
356 | ||
357 | /// <summary> | |
358 | /// True when the application should be started in full screen mode | |
359 | /// </summary> | |
360 | public CECSettingBool StartFullScreen | |
361 | { | |
362 | get | |
363 | { | |
364 | if (!Settings.ContainsKey(ProcessName + "_start_fullscreen")) | |
365 | { | |
366 | CECSettingBool setting = new CECSettingBool(ProcessName + "_start_fullscreen", "Start in full screen mode", true, null); | |
367 | Settings.Load(setting); | |
368 | Settings[ProcessName + "_start_fullscreen"] = setting; | |
369 | } | |
370 | return Settings[ProcessName + "_start_fullscreen"].AsSettingBool; | |
371 | } | |
372 | } | |
373 | ||
374 | protected ControllerTabPage UIControlInternal; | |
375 | public virtual ControllerTabPage UiControl | |
376 | { | |
377 | get { return UIControlInternal ?? (UIControlInternal = new ApplicationControllerUI(this)); } | |
378 | } | |
379 | ||
380 | private CecButtonConfig _buttonConfig; | |
381 | public CecButtonConfig ButtonConfig | |
382 | { | |
324a4cb1 | 383 | get { return _buttonConfig ?? (_buttonConfig = new CecButtonConfig(this)); } |
f017f3c4 LOK |
384 | } |
385 | ||
97401db1 LOK |
386 | public CECSettings Settings |
387 | { | |
388 | get { return Controller.Settings; } | |
389 | } | |
f017f3c4 LOK |
390 | protected DataGridView CecButtonGridView; |
391 | ||
392 | public virtual ApplicationAction DefaultValue(CecKeypress key) | |
393 | { | |
394 | return null; | |
395 | } | |
396 | ||
397 | public virtual bool HasDefaultValue(CecKeypress key) | |
398 | { | |
399 | return DefaultValue(key) != null; | |
400 | } | |
401 | ||
402 | public bool IsInternal { protected set; get; } | |
403 | public bool CanConfigureProcess | |
404 | { | |
405 | get | |
406 | { | |
407 | return !IsInternal; | |
408 | } | |
409 | } | |
f4729954 LOK |
410 | |
411 | private bool _applicationRunning; | |
412 | ||
97401db1 LOK |
413 | protected readonly CECController Controller; |
414 | ||
f017f3c4 LOK |
415 | #endregion |
416 | } | |
417 | } |