don't crash on exit in cectray by preventing a new action from being started
[deb_libcec.git] / src / LibCecTray / ui / CECTray.cs
CommitLineData
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
33using System;
34using System.Windows.Forms;
35using CecSharp;
36using System.IO;
37using LibCECTray.Properties;
38using LibCECTray.controller;
39using LibCECTray.controller.applications;
40using LibCECTray.settings;
fec04a82
LOK
41using Microsoft.Win32;
42using System.Security.Permissions;
07224bf3 43using System.Runtime.InteropServices;
f017f3c4
LOK
44
45namespace LibCECTray.ui
46{
47 /// <summary>
48 /// The tab pages in this application
49 /// </summary>
50 internal enum ConfigTab
51 {
52 Configuration,
53 KeyConfiguration,
54 Tester,
55 Log,
56 WMC,
57 XBMC
58 }
59
60 /// <summary>
61 /// Main LibCecTray GUI
62 /// </summary>
63 partial class CECTray : AsyncForm
64 {
65 public CECTray()
66 {
67 Text = Resources.app_name;
68 InitializeComponent();
f017f3c4
LOK
69 VisibleChanged += delegate
70 {
71 if (!Visible)
72 OnHide();
73 else
74 OnShow();
75 };
07224bf3 76
fec04a82
LOK
77 SystemEvents.SessionEnding += new SessionEndingEventHandler(OnSessionEnding);
78 }
79
80 public void OnSessionEnding(object sender, SessionEndingEventArgs e)
81 {
b9e6e386 82 Controller.CECActions.SuppressUpdates = true;
fec04a82
LOK
83 Controller.Close();
84 }
85
07224bf3
LOK
86 #region Power state change window messages
87 private const int WM_POWERBROADCAST = 0x0218;
88 private const int PBT_APMSUSPEND = 0x0004;
89 private const int PBT_APMRESUMESUSPEND = 0x0007;
90 private const int PBT_APMRESUMECRITICAL = 0x0006;
91 private const int PBT_APMRESUMEAUTOMATIC = 0x0012;
92 private const int PBT_POWERSETTINGCHANGE = 0x8013;
93 private static Guid GUID_SYSTEM_AWAYMODE = new Guid("98a7f580-01f7-48aa-9c0f-44352c29e5c0");
94
95 [StructLayout(LayoutKind.Sequential, Pack = 4)]
96 internal struct POWERBROADCAST_SETTING
97 {
98 public Guid PowerSetting;
99 public uint DataLength;
100 public byte Data;
101 }
102 #endregion
103
104 /// <summary>
105 /// Check for power state changes, and pass up when it's something we don't care about
106 /// </summary>
107 /// <param name="msg">The incoming window message</param>
108 protected override void WndProc(ref Message msg)
fec04a82 109 {
07224bf3 110 if (msg.Msg == WM_POWERBROADCAST)
fec04a82 111 {
07224bf3
LOK
112 switch (msg.WParam.ToInt32())
113 {
114 case PBT_APMSUSPEND:
115 OnSleep();
116 return;
117
118 case PBT_APMRESUMESUSPEND:
119 case PBT_APMRESUMECRITICAL:
120 case PBT_APMRESUMEAUTOMATIC:
121 OnWake();
122 return;
123
124 case PBT_POWERSETTINGCHANGE:
125 {
126 POWERBROADCAST_SETTING pwr = (POWERBROADCAST_SETTING)Marshal.PtrToStructure(msg.LParam, typeof(POWERBROADCAST_SETTING));
127 if (pwr.PowerSetting == GUID_SYSTEM_AWAYMODE && pwr.DataLength == Marshal.SizeOf(typeof(Int32)))
128 {
129 switch (pwr.Data)
130 {
131 case 0:
eb9465c9
LOK
132 // do _not_ wake the pc when away mode is deactivated
133 //OnWake();
134 //return;
07224bf3
LOK
135 case 1:
136 OnSleep();
137 return;
138 default:
139 break;
140 }
141 }
142 }
143 break;
144 default:
145 break;
146 }
fec04a82 147 }
07224bf3
LOK
148
149 // pass up when not handled
150 base.WndProc(ref msg);
151 }
152
153 private void OnWake()
154 {
155 Controller.Initialise();
156 }
157
158 private void OnSleep()
159 {
160 Controller.Close();
f017f3c4
LOK
161 }
162
163 public override sealed string Text
164 {
165 get { return base.Text; }
166 set { base.Text = value; }
167 }
168
97401db1 169 public void Initialise()
f017f3c4 170 {
c4bc8944 171 Controller.Initialise();
f017f3c4
LOK
172 }
173
174 protected override void Dispose(bool disposing)
175 {
176 Hide();
177 if (disposing)
178 {
b9e6e386 179 Controller.CECActions.SuppressUpdates = true;
c4bc8944 180 Controller.Close();
f017f3c4
LOK
181 }
182 if (disposing && (components != null))
183 {
184 components.Dispose();
185 }
186 base.Dispose(disposing);
187 }
188
189 #region Configuration tab
190 /// <summary>
191 /// Replaces the gui controls by the ones that are bound to the settings.
192 /// this is a fugly way to do it, but the gui designer doesn't allow us to ref CECSettings, since it uses symbols from LibCecSharp
193 /// </summary>
194 public void InitialiseSettingsComponent(CECSettings settings)
195 {
196 settings.WakeDevices.ReplaceControls(this, Configuration.Controls, lWakeDevices, cbWakeDevices);
197 settings.PowerOffDevices.ReplaceControls(this, Configuration.Controls, lPowerOff, cbPowerOffDevices);
198 settings.OverridePhysicalAddress.ReplaceControls(this, Configuration.Controls, cbOverrideAddress);
199 settings.OverrideTVVendor.ReplaceControls(this, Configuration.Controls, cbVendorOverride);
200 settings.PhysicalAddress.ReplaceControls(this, Configuration.Controls, tbPhysicalAddress);
201 settings.HDMIPort.ReplaceControls(this, Configuration.Controls, lPortNumber, cbPortNumber);
202 settings.ConnectedDevice.ReplaceControls(this, Configuration.Controls, lConnectedDevice, cbConnectedDevice);
203 settings.ActivateSource.ReplaceControls(this, Configuration.Controls, cbActivateSource);
204 settings.DeviceType.ReplaceControls(this, Configuration.Controls, lDeviceType, cbDeviceType);
205 settings.TVVendor.ReplaceControls(this, Configuration.Controls, cbVendorId);
206 settings.StartHidden.ReplaceControls(this, Configuration.Controls, cbStartMinimised);
207 }
208
209 private void BSaveClick(object sender, EventArgs e)
210 {
c4bc8944 211 Controller.PersistSettings();
f017f3c4
LOK
212 }
213
214 private void BReloadConfigClick(object sender, EventArgs e)
215 {
c4bc8944 216 Controller.ResetDefaultSettings();
f017f3c4
LOK
217 }
218 #endregion
219
220 #region CEC Tester tab
221 delegate void SetActiveDevicesCallback(string[] activeDevices);
222 public void SetActiveDevices(string[] activeDevices)
223 {
224 if (cbCommandDestination.InvokeRequired)
225 {
226 SetActiveDevicesCallback d = SetActiveDevices;
227 try
228 {
229 Invoke(d, new object[] { activeDevices });
230 }
231 catch (Exception) { }
232 }
233 else
234 {
235 cbCommandDestination.Items.Clear();
236 foreach (string item in activeDevices)
237 cbCommandDestination.Items.Add(item);
238 }
239 }
240
241 delegate CecLogicalAddress GetTargetDeviceCallback();
242 private CecLogicalAddress GetTargetDevice()
243 {
244 if (cbCommandDestination.InvokeRequired)
245 {
246 GetTargetDeviceCallback d = GetTargetDevice;
247 CecLogicalAddress retval = CecLogicalAddress.Unknown;
248 try
249 {
250 retval = (CecLogicalAddress)Invoke(d, new object[] { });
251 }
252 catch (Exception) { }
253 return retval;
254 }
255
256 return CECSettingLogicalAddresses.GetLogicalAddressFromString(cbCommandDestination.Text);
257 }
258
259 private void BSendImageViewOnClick(object sender, EventArgs e)
260 {
c4bc8944 261 Controller.CECActions.SendImageViewOn(GetTargetDevice());
f017f3c4
LOK
262 }
263
264 private void BStandbyClick(object sender, EventArgs e)
265 {
c4bc8944 266 Controller.CECActions.SendStandby(GetTargetDevice());
f017f3c4
LOK
267 }
268
269 private void BScanClick(object sender, EventArgs e)
270 {
c4bc8944 271 Controller.CECActions.ShowDeviceInfo(GetTargetDevice());
f017f3c4
LOK
272 }
273
274 private void BActivateSourceClick(object sender, EventArgs e)
275 {
c4bc8944 276 Controller.CECActions.ActivateSource(GetTargetDevice());
f017f3c4
LOK
277 }
278
279 private void CbCommandDestinationSelectedIndexChanged(object sender, EventArgs e)
280 {
281 bool enableVolumeButtons = (GetTargetDevice() == CecLogicalAddress.AudioSystem);
282 bVolUp.Enabled = enableVolumeButtons;
283 bVolDown.Enabled = enableVolumeButtons;
284 bMute.Enabled = enableVolumeButtons;
285 bActivateSource.Enabled = (GetTargetDevice() != CecLogicalAddress.Broadcast);
286 bScan.Enabled = (GetTargetDevice() != CecLogicalAddress.Broadcast);
287 }
288
289 private void BVolUpClick(object sender, EventArgs e)
290 {
c4bc8944 291 Controller.Lib.VolumeUp(true);
f017f3c4
LOK
292 }
293
294 private void BVolDownClick(object sender, EventArgs e)
295 {
c4bc8944 296 Controller.Lib.VolumeDown(true);
f017f3c4
LOK
297 }
298
299 private void BMuteClick(object sender, EventArgs e)
300 {
c4bc8944 301 Controller.Lib.MuteAudio(true);
f017f3c4
LOK
302 }
303
304 private void BRescanDevicesClick(object sender, EventArgs e)
305 {
c4bc8944 306 Controller.CECActions.RescanDevices();
f017f3c4
LOK
307 }
308 #endregion
309
310 #region Log tab
311 delegate void UpdateLogCallback();
312 private void UpdateLog()
313 {
314 if (tbLog.InvokeRequired)
315 {
316 UpdateLogCallback d = UpdateLog;
317 try
318 {
319 Invoke(d, new object[] { });
320 }
321 catch (Exception) { }
322 }
323 else
324 {
325 tbLog.Text = _log;
326 tbLog.Select(tbLog.Text.Length, 0);
327 tbLog.ScrollToCaret();
328 }
329 }
330
331 public void AddLogMessage(CecLogMessage message)
332 {
333 string strLevel = "";
334 bool display = false;
335 switch (message.Level)
336 {
337 case CecLogLevel.Error:
338 strLevel = "ERROR: ";
339 display = cbLogError.Checked;
340 break;
341 case CecLogLevel.Warning:
342 strLevel = "WARNING: ";
343 display = cbLogWarning.Checked;
344 break;
345 case CecLogLevel.Notice:
346 strLevel = "NOTICE: ";
347 display = cbLogNotice.Checked;
348 break;
349 case CecLogLevel.Traffic:
350 strLevel = "TRAFFIC: ";
351 display = cbLogTraffic.Checked;
352 break;
353 case CecLogLevel.Debug:
354 strLevel = "DEBUG: ";
355 display = cbLogDebug.Checked;
356 break;
357 }
358
359 if (display)
360 {
361 string strLog = string.Format("{0} {1,16} {2}", strLevel, message.Time, message.Message) + Environment.NewLine;
362 AddLogMessage(strLog);
363 }
364 }
365
366 public void AddLogMessage(string message)
367 {
368 _log += message;
369
370 if (_selectedTab == ConfigTab.Log)
371 UpdateLog();
372 }
373
374 private void BClearLogClick(object sender, EventArgs e)
375 {
376 _log = string.Empty;
377 UpdateLog();
378 }
379
380 private void BSaveLogClick(object sender, EventArgs e)
381 {
382 SaveFileDialog dialog = new SaveFileDialog
383 {
384 Title = Resources.where_do_you_want_to_store_the_log,
385 InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
386 FileName = Resources.cec_log_filename,
387 Filter = Resources.cec_log_filter,
388 FilterIndex = 1
389 };
390
391 if (dialog.ShowDialog() == DialogResult.OK)
392 {
393 FileStream fs = (FileStream)dialog.OpenFile();
394 if (!fs.CanWrite)
395 {
396 MessageBox.Show(string.Format(Resources.cannot_open_for_writing, dialog.FileName), Resources.app_name, MessageBoxButtons.OK, MessageBoxIcon.Error);
397 }
398 else
399 {
400 StreamWriter writer = new StreamWriter(fs);
401 writer.Write(_log);
402 writer.Close();
403 fs.Close();
404 fs.Dispose();
405 MessageBox.Show(string.Format(Resources.log_stored_as, dialog.FileName), Resources.app_name, MessageBoxButtons.OK, MessageBoxIcon.Information);
406 }
407 }
408 }
409 #endregion
410
411 #region Tray icon and window controls
412 private void HideToolStripMenuItemClick(object sender, EventArgs e)
413 {
414 ShowHideToggle();
415 }
416
417 private void CloseToolStripMenuItemClick(object sender, EventArgs e)
418 {
419 Dispose();
420 }
421
422 private void AboutToolStripMenuItemClick(object sender, EventArgs e)
423 {
c4bc8944 424 (new About(Controller.LibServerVersion, Controller.LibClientVersion, Controller.LibInfo)).ShowDialog();
f017f3c4
LOK
425 }
426
427 private void AdvancedModeToolStripMenuItemClick(object sender, EventArgs e)
428 {
c4bc8944 429 Controller.Settings.AdvancedMode.Value = !advancedModeToolStripMenuItem.Checked;
f017f3c4
LOK
430 ShowHideAdvanced(!advancedModeToolStripMenuItem.Checked);
431 }
432
433 private void BCancelClick(object sender, EventArgs e)
434 {
435 Dispose();
436 }
437
438 private void TrayIconClick(object sender, EventArgs e)
439 {
440 if (e is MouseEventArgs && (e as MouseEventArgs).Button == MouseButtons.Left)
441 ShowHideToggle();
442 }
443
444 public void OnHide()
445 {
446 ShowInTaskbar = false;
447 Visible = false;
448 tsMenuShowHide.Text = Resources.show;
449 }
450
451 public void OnShow()
452 {
453 ShowInTaskbar = true;
454 WindowState = FormWindowState.Normal;
455 Activate();
456 tsMenuShowHide.Text = Resources.hide;
457 }
458
459 private void ShowHideToggle()
460 {
461 if (Visible && WindowState != FormWindowState.Minimized)
462 {
c4bc8944 463 Controller.Settings.StartHidden.Value = true;
f017f3c4
LOK
464 Hide();
465 }
466 else
467 {
c4bc8944 468 Controller.Settings.StartHidden.Value = false;
f017f3c4
LOK
469 Show();
470 }
471 }
472
473 private void TsMenuCloseClick(object sender, EventArgs e)
474 {
475 Dispose();
476 }
477
478 private void CECTrayResize(object sender, EventArgs e)
479 {
480 if (WindowState == FormWindowState.Minimized)
177e3a7b 481 Hide();
f017f3c4 482 else
177e3a7b 483 Show();
f017f3c4
LOK
484 }
485
486 private void TsMenuShowHideClick(object sender, EventArgs e)
487 {
488 ShowHideToggle();
489 }
490
491 public void ShowHideAdvanced(bool setTo)
492 {
493 if (setTo)
494 {
495 tsAdvanced.Checked = true;
496 advancedModeToolStripMenuItem.Checked = true;
497 SuspendLayout();
498 if (!tabPanel.Controls.Contains(tbTestCommands))
499 TabControls.Add(tbTestCommands);
500 if (!tabPanel.Controls.Contains(LogOutput))
501 TabControls.Add(LogOutput);
502 ResumeLayout();
503 }
504 else
505 {
506 tsAdvanced.Checked = false;
507 advancedModeToolStripMenuItem.Checked = false;
508 SuspendLayout();
509 tabPanel.Controls.Remove(tbTestCommands);
510 tabPanel.Controls.Remove(LogOutput);
511 ResumeLayout();
512 }
513 }
514
515 private void TsAdvancedClick(object sender, EventArgs e)
516 {
c4bc8944 517 Controller.Settings.AdvancedMode.Value = !tsAdvanced.Checked;
f017f3c4
LOK
518 ShowHideAdvanced(!tsAdvanced.Checked);
519 }
520
521 public void SetStatusText(string status)
522 {
523 SetControlText(lStatus, status);
524 }
525
526 public void SetProgressBar(int progress, bool visible)
527 {
528 SetControlVisible(pProgress, visible);
529 SetProgressValue(pProgress, progress);
530 }
531
532 public void SetControlsEnabled(bool val)
533 {
534 //main tab
535 SetControlEnabled(bClose, val);
536 SetControlEnabled(bSaveConfig, val);
537 SetControlEnabled(bReloadConfig, val);
538
539 //tester tab
540 SetControlEnabled(bRescanDevices, val);
541 SetControlEnabled(bSendImageViewOn, val);
542 SetControlEnabled(bStandby, val);
543 SetControlEnabled(bActivateSource, val);
544 SetControlEnabled(bScan, val);
545
546 bool enableVolumeButtons = (GetTargetDevice() == CecLogicalAddress.AudioSystem) && val;
547 SetControlEnabled(bVolUp, enableVolumeButtons);
548 SetControlEnabled(bVolDown, enableVolumeButtons);
549 SetControlEnabled(bMute, enableVolumeButtons);
550 }
551
552 private void TabControl1SelectedIndexChanged(object sender, EventArgs e)
553 {
554 switch (tabPanel.TabPages[tabPanel.SelectedIndex].Name)
555 {
556 case "tbTestCommands":
557 _selectedTab = ConfigTab.Tester;
558 break;
559 case "LogOutput":
560 _selectedTab = ConfigTab.Log;
561 UpdateLog();
562 break;
563 default:
564 _selectedTab = ConfigTab.Configuration;
565 break;
566 }
567 }
568 #endregion
569
570 #region Class members
571 private ConfigTab _selectedTab = ConfigTab.Configuration;
572 private string _log = string.Empty;
c4bc8944 573 private CECController _controller;
49689754
LOK
574 public CECController Controller
575 {
97401db1
LOK
576 get
577 {
578 return _controller ?? (_controller = new CECController(this));
579 }
49689754 580 }
f017f3c4
LOK
581 public Control.ControlCollection TabControls
582 {
583 get { return tabPanel.Controls; }
584 }
585 public string SelectedTabName
586 {
587 get { return GetSelectedTabName(tabPanel, tabPanel.TabPages); }
588 }
589 #endregion
590
591 private void AddNewApplicationToolStripMenuItemClick(object sender, EventArgs e)
592 {
c4bc8944
LOK
593 ConfigureApplication appConfig = new ConfigureApplication(Controller.Settings, Controller);
594 Controller.DisplayDialog(appConfig, false);
f017f3c4
LOK
595 }
596 }
597}