2 * This file is part of the libCEC(R) library.
4 * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved.
5 * libCEC(R) is an original work, containing original code.
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
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.
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.
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.
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
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/
34 using System.Windows.Forms;
37 using LibCECTray.Properties;
38 using LibCECTray.controller;
39 using LibCECTray.controller.applications;
40 using LibCECTray.settings;
41 using Microsoft.Win32;
42 using System.Security.Permissions;
43 using System.Runtime.InteropServices;
44 using System.Threading;
46 namespace LibCECTray.ui
49 /// Main LibCecTray GUI
51 partial class CECTray : AsyncForm
55 Text = Resources.app_name;
56 InitializeComponent();
58 _sstimer.Interval = 5000;
59 _sstimer.Tick += ScreensaverActiveCheck;
60 _sstimer.Enabled = false;
62 _lastScreensaverActivated = DateTime.Now;
64 VisibleChanged += delegate
72 SystemEvents.SessionEnding += new SessionEndingEventHandler(OnSessionEnding);
75 public void OnSessionEnding(object sender, SessionEndingEventArgs e)
77 Controller.CECActions.SuppressUpdates = true;
81 #region Power state change window messages
82 private const int WM_POWERBROADCAST = 0x0218;
83 private const int WM_SYSCOMMAND = 0x0112;
85 private const int PBT_APMSUSPEND = 0x0004;
86 private const int PBT_APMRESUMESUSPEND = 0x0007;
87 private const int PBT_APMRESUMECRITICAL = 0x0006;
88 private const int PBT_APMRESUMEAUTOMATIC = 0x0012;
89 private const int PBT_POWERSETTINGCHANGE = 0x8013;
91 private static Guid GUID_SYSTEM_AWAYMODE = new Guid("98a7f580-01f7-48aa-9c0f-44352c29e5c0");
93 private const int SC_SCREENSAVE = 0xF140;
94 private const int SPI_GETSCREENSAVERRUNNING = 0x0072;
96 [DllImport("user32.dll", SetLastError = true)]
97 static extern bool SystemParametersInfo(int action, int param, ref int retval, int updini);
99 [StructLayout(LayoutKind.Sequential, Pack = 4)]
100 internal struct POWERBROADCAST_SETTING
102 public Guid PowerSetting;
103 public uint DataLength;
109 /// Check for power state changes, and pass up when it's something we don't care about
111 /// <param name="msg">The incoming window message</param>
112 protected override void WndProc(ref Message msg)
114 if (msg.Msg == WM_SYSCOMMAND && (msg.WParam.ToInt32() & 0xfff0) == SC_SCREENSAVE)
116 // there's no event for screensaver exit
117 if (!_sstimer.Enabled)
119 // guard against screensaver failing, and resulting in power up and down spam to the tv
120 TimeSpan diff = DateTime.Now - _lastScreensaverActivated;
121 if (diff.TotalSeconds > 60)
123 _sstimer.Enabled = true;
124 _lastScreensaverActivated = DateTime.Now;
125 Controller.CECActions.SendStandby(CecLogicalAddress.Broadcast);
129 else if (msg.Msg == WM_POWERBROADCAST)
131 switch (msg.WParam.ToInt32())
137 case PBT_APMRESUMESUSPEND:
138 case PBT_APMRESUMECRITICAL:
139 case PBT_APMRESUMEAUTOMATIC:
143 case PBT_POWERSETTINGCHANGE:
145 POWERBROADCAST_SETTING pwr = (POWERBROADCAST_SETTING)Marshal.PtrToStructure(msg.LParam, typeof(POWERBROADCAST_SETTING));
146 if (pwr.PowerSetting == GUID_SYSTEM_AWAYMODE && pwr.DataLength == Marshal.SizeOf(typeof(Int32)))
151 // do _not_ wake the pc when away mode is deactivated
155 Controller.CECActions.SendStandby(CecLogicalAddress.Broadcast);
168 // pass up when not handled
169 base.WndProc(ref msg);
172 private void ScreensaverActiveCheck(object sender, EventArgs e)
174 if (!IsScreensaverActive())
176 _sstimer.Enabled = false;
177 Controller.CECActions.ActivateSource();
181 private bool IsScreensaverActive()
184 SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, ref active, 0);
188 private void OnWake()
190 Controller.Initialise();
193 private void OnSleep()
195 Controller.CECActions.SuppressUpdates = true;
196 AsyncDisconnect dc = new AsyncDisconnect(Controller);
197 (new Thread(dc.Process)).Start();
200 public override sealed string Text
202 get { return base.Text; }
203 set { base.Text = value; }
206 public void Initialise()
208 Controller.Initialise();
211 protected override void Dispose(bool disposing)
218 if (disposing && (components != null))
220 components.Dispose();
222 base.Dispose(disposing);
225 #region Configuration tab
227 /// Replaces the gui controls by the ones that are bound to the settings.
228 /// 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
230 public void InitialiseSettingsComponent(CECSettings settings)
232 settings.WakeDevices.ReplaceControls(this, Configuration.Controls, lWakeDevices, cbWakeDevices);
233 settings.PowerOffDevices.ReplaceControls(this, Configuration.Controls, lPowerOff, cbPowerOffDevices);
234 settings.OverridePhysicalAddress.ReplaceControls(this, Configuration.Controls, cbOverrideAddress);
235 settings.OverrideTVVendor.ReplaceControls(this, Configuration.Controls, cbVendorOverride);
236 settings.PhysicalAddress.ReplaceControls(this, Configuration.Controls, tbPhysicalAddress);
237 settings.HDMIPort.ReplaceControls(this, Configuration.Controls, lPortNumber, cbPortNumber);
238 settings.ConnectedDevice.ReplaceControls(this, Configuration.Controls, lConnectedDevice, cbConnectedDevice);
239 settings.ActivateSource.ReplaceControls(this, Configuration.Controls, cbActivateSource);
240 settings.DeviceType.ReplaceControls(this, Configuration.Controls, lDeviceType, cbDeviceType);
241 settings.TVVendor.ReplaceControls(this, Configuration.Controls, cbVendorId);
242 settings.StartHidden.ReplaceControls(this, Configuration.Controls, cbStartMinimised);
245 private void BSaveClick(object sender, EventArgs e)
247 Controller.PersistSettings();
250 private void BReloadConfigClick(object sender, EventArgs e)
252 Controller.ResetDefaultSettings();
256 #region CEC Tester tab
257 delegate void SetActiveDevicesCallback(string[] activeDevices);
258 public void SetActiveDevices(string[] activeDevices)
260 if (cbCommandDestination.InvokeRequired)
262 SetActiveDevicesCallback d = SetActiveDevices;
265 Invoke(d, new object[] { activeDevices });
267 catch (Exception) { }
271 cbCommandDestination.Items.Clear();
272 foreach (string item in activeDevices)
273 cbCommandDestination.Items.Add(item);
277 delegate CecLogicalAddress GetTargetDeviceCallback();
278 private CecLogicalAddress GetTargetDevice()
280 if (cbCommandDestination.InvokeRequired)
282 GetTargetDeviceCallback d = GetTargetDevice;
283 CecLogicalAddress retval = CecLogicalAddress.Unknown;
286 retval = (CecLogicalAddress)Invoke(d, new object[] { });
288 catch (Exception) { }
292 return CECSettingLogicalAddresses.GetLogicalAddressFromString(cbCommandDestination.Text);
295 private void BSendImageViewOnClick(object sender, EventArgs e)
297 Controller.CECActions.SendImageViewOn(GetTargetDevice());
300 private void BStandbyClick(object sender, EventArgs e)
302 Controller.CECActions.SendStandby(GetTargetDevice());
305 private void BScanClick(object sender, EventArgs e)
307 Controller.CECActions.ShowDeviceInfo(GetTargetDevice());
310 private void BActivateSourceClick(object sender, EventArgs e)
312 Controller.CECActions.SetStreamPath(GetTargetDevice());
315 private void CbCommandDestinationSelectedIndexChanged(object sender, EventArgs e)
317 bool enableVolumeButtons = (GetTargetDevice() == CecLogicalAddress.AudioSystem);
318 bVolUp.Enabled = enableVolumeButtons;
319 bVolDown.Enabled = enableVolumeButtons;
320 bMute.Enabled = enableVolumeButtons;
321 bActivateSource.Enabled = (GetTargetDevice() != CecLogicalAddress.Broadcast);
322 bScan.Enabled = (GetTargetDevice() != CecLogicalAddress.Broadcast);
325 private void BVolUpClick(object sender, EventArgs e)
327 Controller.Lib.VolumeUp(true);
330 private void BVolDownClick(object sender, EventArgs e)
332 Controller.Lib.VolumeDown(true);
335 private void BMuteClick(object sender, EventArgs e)
337 Controller.Lib.MuteAudio(true);
340 private void BRescanDevicesClick(object sender, EventArgs e)
342 Controller.CECActions.RescanDevices();
347 delegate void UpdateLogCallback();
348 private void UpdateLog()
350 if (tbLog.InvokeRequired)
352 UpdateLogCallback d = UpdateLog;
355 Invoke(d, new object[] { });
357 catch (Exception) { }
362 tbLog.Select(tbLog.Text.Length, 0);
363 tbLog.ScrollToCaret();
367 public void AddLogMessage(CecLogMessage message)
369 string strLevel = "";
370 bool display = false;
371 switch (message.Level)
373 case CecLogLevel.Error:
374 strLevel = "ERROR: ";
375 display = cbLogError.Checked;
377 case CecLogLevel.Warning:
378 strLevel = "WARNING: ";
379 display = cbLogWarning.Checked;
381 case CecLogLevel.Notice:
382 strLevel = "NOTICE: ";
383 display = cbLogNotice.Checked;
385 case CecLogLevel.Traffic:
386 strLevel = "TRAFFIC: ";
387 display = cbLogTraffic.Checked;
389 case CecLogLevel.Debug:
390 strLevel = "DEBUG: ";
391 display = cbLogDebug.Checked;
397 string strLog = string.Format("{0} {1,16} {2}", strLevel, message.Time, message.Message) + Environment.NewLine;
398 AddLogMessage(strLog);
402 public void AddLogMessage(string message)
406 if (_selectedTab == ConfigTab.Log)
410 private void BClearLogClick(object sender, EventArgs e)
416 private void BSaveLogClick(object sender, EventArgs e)
418 SaveFileDialog dialog = new SaveFileDialog
420 Title = Resources.where_do_you_want_to_store_the_log,
421 InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
422 FileName = Resources.cec_log_filename,
423 Filter = Resources.cec_log_filter,
427 if (dialog.ShowDialog() == DialogResult.OK)
429 FileStream fs = (FileStream)dialog.OpenFile();
432 MessageBox.Show(string.Format(Resources.cannot_open_for_writing, dialog.FileName), Resources.app_name, MessageBoxButtons.OK, MessageBoxIcon.Error);
436 StreamWriter writer = new StreamWriter(fs);
441 MessageBox.Show(string.Format(Resources.log_stored_as, dialog.FileName), Resources.app_name, MessageBoxButtons.OK, MessageBoxIcon.Information);
447 #region Tray icon and window controls
448 private void HideToolStripMenuItemClick(object sender, EventArgs e)
453 private void CloseToolStripMenuItemClick(object sender, EventArgs e)
458 private void AboutToolStripMenuItemClick(object sender, EventArgs e)
460 (new About(Controller.LibServerVersion, Controller.LibClientVersion, Controller.LibInfo)).ShowDialog();
463 private void AdvancedModeToolStripMenuItemClick(object sender, EventArgs e)
465 Controller.Settings.AdvancedMode.Value = !advancedModeToolStripMenuItem.Checked;
466 ShowHideAdvanced(!advancedModeToolStripMenuItem.Checked);
469 private void BCancelClick(object sender, EventArgs e)
474 private void TrayIconClick(object sender, EventArgs e)
476 if (e is MouseEventArgs && (e as MouseEventArgs).Button == MouseButtons.Left)
482 ShowInTaskbar = false;
484 tsMenuShowHide.Text = Resources.show;
489 ShowInTaskbar = true;
490 WindowState = FormWindowState.Normal;
492 tsMenuShowHide.Text = Resources.hide;
495 private void ShowHideToggle()
497 if (Visible && WindowState != FormWindowState.Minimized)
499 Controller.Settings.StartHidden.Value = true;
504 Controller.Settings.StartHidden.Value = false;
509 private void TsMenuCloseClick(object sender, EventArgs e)
514 private void CECTrayResize(object sender, EventArgs e)
516 if (WindowState == FormWindowState.Minimized)
522 private void TsMenuShowHideClick(object sender, EventArgs e)
527 public void ShowHideAdvanced(bool setTo)
531 tsAdvanced.Checked = true;
532 advancedModeToolStripMenuItem.Checked = true;
534 if (!tabPanel.Controls.Contains(tbTestCommands))
535 TabControls.Add(tbTestCommands);
536 if (!tabPanel.Controls.Contains(LogOutput))
537 TabControls.Add(LogOutput);
542 tsAdvanced.Checked = false;
543 advancedModeToolStripMenuItem.Checked = false;
545 tabPanel.Controls.Remove(tbTestCommands);
546 tabPanel.Controls.Remove(LogOutput);
551 private void TsAdvancedClick(object sender, EventArgs e)
553 Controller.Settings.AdvancedMode.Value = !tsAdvanced.Checked;
554 ShowHideAdvanced(!tsAdvanced.Checked);
557 public void SetStatusText(string status)
559 SetControlText(lStatus, status);
562 public void SetProgressBar(int progress, bool visible)
564 SetControlVisible(pProgress, visible);
565 SetProgressValue(pProgress, progress);
568 public void SetControlsEnabled(bool val)
571 SetControlEnabled(bClose, val);
572 SetControlEnabled(bSaveConfig, val);
573 SetControlEnabled(bReloadConfig, val);
576 SetControlEnabled(bRescanDevices, val);
577 SetControlEnabled(bSendImageViewOn, val);
578 SetControlEnabled(bStandby, val);
579 SetControlEnabled(bActivateSource, val);
580 SetControlEnabled(bScan, val);
582 bool enableVolumeButtons = (GetTargetDevice() == CecLogicalAddress.AudioSystem) && val;
583 SetControlEnabled(bVolUp, enableVolumeButtons);
584 SetControlEnabled(bVolDown, enableVolumeButtons);
585 SetControlEnabled(bMute, enableVolumeButtons);
588 private void TabControl1SelectedIndexChanged(object sender, EventArgs e)
590 switch (tabPanel.TabPages[tabPanel.SelectedIndex].Name)
592 case "tbTestCommands":
593 _selectedTab = ConfigTab.Tester;
596 _selectedTab = ConfigTab.Log;
600 _selectedTab = ConfigTab.Configuration;
606 #region Class members
607 private ConfigTab _selectedTab = ConfigTab.Configuration;
608 private string _log = string.Empty;
609 private CECController _controller;
610 public CECController Controller
614 return _controller ?? (_controller = new CECController(this));
617 public Control.ControlCollection TabControls
619 get { return tabPanel.Controls; }
621 public string SelectedTabName
623 get { return GetSelectedTabName(tabPanel, tabPanel.TabPages); }
626 private System.Windows.Forms.Timer _sstimer = new System.Windows.Forms.Timer();
627 private DateTime _lastScreensaverActivated;
630 private void AddNewApplicationToolStripMenuItemClick(object sender, EventArgs e)
632 ConfigureApplication appConfig = new ConfigureApplication(Controller.Settings, Controller);
633 Controller.DisplayDialog(appConfig, false);
638 /// The tab pages in this application
640 internal enum ConfigTab
650 class AsyncDisconnect
652 public AsyncDisconnect(CECController controller)
654 _controller = controller;
657 public void Process()
662 private CECController _controller;