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 VisibleChanged += delegate
70 SystemEvents.SessionEnding += new SessionEndingEventHandler(OnSessionEnding);
73 public void OnSessionEnding(object sender, SessionEndingEventArgs e)
75 Controller.CECActions.SuppressUpdates = true;
79 #region Power state change window messages
80 private const int WM_POWERBROADCAST = 0x0218;
81 private const int WM_SYSCOMMAND = 0x0112;
83 private const int PBT_APMSUSPEND = 0x0004;
84 private const int PBT_APMRESUMESUSPEND = 0x0007;
85 private const int PBT_APMRESUMECRITICAL = 0x0006;
86 private const int PBT_APMRESUMEAUTOMATIC = 0x0012;
87 private const int PBT_POWERSETTINGCHANGE = 0x8013;
89 private static Guid GUID_SYSTEM_AWAYMODE = new Guid("98a7f580-01f7-48aa-9c0f-44352c29e5c0");
91 private const int SC_SCREENSAVE = 0xF140;
92 private const int SPI_GETSCREENSAVERRUNNING = 0x0072;
94 [DllImport("user32.dll", SetLastError = true)]
95 static extern bool SystemParametersInfo(int action, int param, ref int retval, int updini);
97 [StructLayout(LayoutKind.Sequential, Pack = 4)]
98 internal struct POWERBROADCAST_SETTING
100 public Guid PowerSetting;
101 public uint DataLength;
107 /// Check for power state changes, and pass up when it's something we don't care about
109 /// <param name="msg">The incoming window message</param>
110 protected override void WndProc(ref Message msg)
112 if (msg.Msg == WM_SYSCOMMAND && (msg.WParam.ToInt32() & 0xfff0) == SC_SCREENSAVE)
114 // there's no event for screensaver exit
115 if (!_sstimer.Enabled)
117 _sstimer.Enabled = true;
118 Controller.CECActions.SendStandby(CecLogicalAddress.Broadcast);
121 else if (msg.Msg == WM_POWERBROADCAST)
123 switch (msg.WParam.ToInt32())
129 case PBT_APMRESUMESUSPEND:
130 case PBT_APMRESUMECRITICAL:
131 case PBT_APMRESUMEAUTOMATIC:
135 case PBT_POWERSETTINGCHANGE:
137 POWERBROADCAST_SETTING pwr = (POWERBROADCAST_SETTING)Marshal.PtrToStructure(msg.LParam, typeof(POWERBROADCAST_SETTING));
138 if (pwr.PowerSetting == GUID_SYSTEM_AWAYMODE && pwr.DataLength == Marshal.SizeOf(typeof(Int32)))
143 // do _not_ wake the pc when away mode is deactivated
160 // pass up when not handled
161 base.WndProc(ref msg);
164 private void ScreensaverActiveCheck(object sender, EventArgs e)
166 if (!IsScreensaverActive())
168 _sstimer.Enabled = false;
169 Controller.CECActions.ActivateSource();
173 private bool IsScreensaverActive()
176 SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, ref active, 0);
180 private void OnWake()
182 Controller.Initialise();
185 private void OnSleep()
187 Controller.CECActions.SuppressUpdates = true;
188 AsyncDisconnect dc = new AsyncDisconnect(Controller);
189 (new Thread(dc.Process)).Start();
192 public override sealed string Text
194 get { return base.Text; }
195 set { base.Text = value; }
198 public void Initialise()
200 Controller.Initialise();
203 protected override void Dispose(bool disposing)
210 if (disposing && (components != null))
212 components.Dispose();
214 base.Dispose(disposing);
217 #region Configuration tab
219 /// Replaces the gui controls by the ones that are bound to the settings.
220 /// 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
222 public void InitialiseSettingsComponent(CECSettings settings)
224 settings.WakeDevices.ReplaceControls(this, Configuration.Controls, lWakeDevices, cbWakeDevices);
225 settings.PowerOffDevices.ReplaceControls(this, Configuration.Controls, lPowerOff, cbPowerOffDevices);
226 settings.OverridePhysicalAddress.ReplaceControls(this, Configuration.Controls, cbOverrideAddress);
227 settings.OverrideTVVendor.ReplaceControls(this, Configuration.Controls, cbVendorOverride);
228 settings.PhysicalAddress.ReplaceControls(this, Configuration.Controls, tbPhysicalAddress);
229 settings.HDMIPort.ReplaceControls(this, Configuration.Controls, lPortNumber, cbPortNumber);
230 settings.ConnectedDevice.ReplaceControls(this, Configuration.Controls, lConnectedDevice, cbConnectedDevice);
231 settings.ActivateSource.ReplaceControls(this, Configuration.Controls, cbActivateSource);
232 settings.DeviceType.ReplaceControls(this, Configuration.Controls, lDeviceType, cbDeviceType);
233 settings.TVVendor.ReplaceControls(this, Configuration.Controls, cbVendorId);
234 settings.StartHidden.ReplaceControls(this, Configuration.Controls, cbStartMinimised);
237 private void BSaveClick(object sender, EventArgs e)
239 Controller.PersistSettings();
242 private void BReloadConfigClick(object sender, EventArgs e)
244 Controller.ResetDefaultSettings();
248 #region CEC Tester tab
249 delegate void SetActiveDevicesCallback(string[] activeDevices);
250 public void SetActiveDevices(string[] activeDevices)
252 if (cbCommandDestination.InvokeRequired)
254 SetActiveDevicesCallback d = SetActiveDevices;
257 Invoke(d, new object[] { activeDevices });
259 catch (Exception) { }
263 cbCommandDestination.Items.Clear();
264 foreach (string item in activeDevices)
265 cbCommandDestination.Items.Add(item);
269 delegate CecLogicalAddress GetTargetDeviceCallback();
270 private CecLogicalAddress GetTargetDevice()
272 if (cbCommandDestination.InvokeRequired)
274 GetTargetDeviceCallback d = GetTargetDevice;
275 CecLogicalAddress retval = CecLogicalAddress.Unknown;
278 retval = (CecLogicalAddress)Invoke(d, new object[] { });
280 catch (Exception) { }
284 return CECSettingLogicalAddresses.GetLogicalAddressFromString(cbCommandDestination.Text);
287 private void BSendImageViewOnClick(object sender, EventArgs e)
289 Controller.CECActions.SendImageViewOn(GetTargetDevice());
292 private void BStandbyClick(object sender, EventArgs e)
294 Controller.CECActions.SendStandby(GetTargetDevice());
297 private void BScanClick(object sender, EventArgs e)
299 Controller.CECActions.ShowDeviceInfo(GetTargetDevice());
302 private void BActivateSourceClick(object sender, EventArgs e)
304 Controller.CECActions.SetStreamPath(GetTargetDevice());
307 private void CbCommandDestinationSelectedIndexChanged(object sender, EventArgs e)
309 bool enableVolumeButtons = (GetTargetDevice() == CecLogicalAddress.AudioSystem);
310 bVolUp.Enabled = enableVolumeButtons;
311 bVolDown.Enabled = enableVolumeButtons;
312 bMute.Enabled = enableVolumeButtons;
313 bActivateSource.Enabled = (GetTargetDevice() != CecLogicalAddress.Broadcast);
314 bScan.Enabled = (GetTargetDevice() != CecLogicalAddress.Broadcast);
317 private void BVolUpClick(object sender, EventArgs e)
319 Controller.Lib.VolumeUp(true);
322 private void BVolDownClick(object sender, EventArgs e)
324 Controller.Lib.VolumeDown(true);
327 private void BMuteClick(object sender, EventArgs e)
329 Controller.Lib.MuteAudio(true);
332 private void BRescanDevicesClick(object sender, EventArgs e)
334 Controller.CECActions.RescanDevices();
339 delegate void UpdateLogCallback();
340 private void UpdateLog()
342 if (tbLog.InvokeRequired)
344 UpdateLogCallback d = UpdateLog;
347 Invoke(d, new object[] { });
349 catch (Exception) { }
354 tbLog.Select(tbLog.Text.Length, 0);
355 tbLog.ScrollToCaret();
359 public void AddLogMessage(CecLogMessage message)
361 string strLevel = "";
362 bool display = false;
363 switch (message.Level)
365 case CecLogLevel.Error:
366 strLevel = "ERROR: ";
367 display = cbLogError.Checked;
369 case CecLogLevel.Warning:
370 strLevel = "WARNING: ";
371 display = cbLogWarning.Checked;
373 case CecLogLevel.Notice:
374 strLevel = "NOTICE: ";
375 display = cbLogNotice.Checked;
377 case CecLogLevel.Traffic:
378 strLevel = "TRAFFIC: ";
379 display = cbLogTraffic.Checked;
381 case CecLogLevel.Debug:
382 strLevel = "DEBUG: ";
383 display = cbLogDebug.Checked;
389 string strLog = string.Format("{0} {1,16} {2}", strLevel, message.Time, message.Message) + Environment.NewLine;
390 AddLogMessage(strLog);
394 public void AddLogMessage(string message)
398 if (_selectedTab == ConfigTab.Log)
402 private void BClearLogClick(object sender, EventArgs e)
408 private void BSaveLogClick(object sender, EventArgs e)
410 SaveFileDialog dialog = new SaveFileDialog
412 Title = Resources.where_do_you_want_to_store_the_log,
413 InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
414 FileName = Resources.cec_log_filename,
415 Filter = Resources.cec_log_filter,
419 if (dialog.ShowDialog() == DialogResult.OK)
421 FileStream fs = (FileStream)dialog.OpenFile();
424 MessageBox.Show(string.Format(Resources.cannot_open_for_writing, dialog.FileName), Resources.app_name, MessageBoxButtons.OK, MessageBoxIcon.Error);
428 StreamWriter writer = new StreamWriter(fs);
433 MessageBox.Show(string.Format(Resources.log_stored_as, dialog.FileName), Resources.app_name, MessageBoxButtons.OK, MessageBoxIcon.Information);
439 #region Tray icon and window controls
440 private void HideToolStripMenuItemClick(object sender, EventArgs e)
445 private void CloseToolStripMenuItemClick(object sender, EventArgs e)
450 private void AboutToolStripMenuItemClick(object sender, EventArgs e)
452 (new About(Controller.LibServerVersion, Controller.LibClientVersion, Controller.LibInfo)).ShowDialog();
455 private void AdvancedModeToolStripMenuItemClick(object sender, EventArgs e)
457 Controller.Settings.AdvancedMode.Value = !advancedModeToolStripMenuItem.Checked;
458 ShowHideAdvanced(!advancedModeToolStripMenuItem.Checked);
461 private void BCancelClick(object sender, EventArgs e)
466 private void TrayIconClick(object sender, EventArgs e)
468 if (e is MouseEventArgs && (e as MouseEventArgs).Button == MouseButtons.Left)
474 ShowInTaskbar = false;
476 tsMenuShowHide.Text = Resources.show;
481 ShowInTaskbar = true;
482 WindowState = FormWindowState.Normal;
484 tsMenuShowHide.Text = Resources.hide;
487 private void ShowHideToggle()
489 if (Visible && WindowState != FormWindowState.Minimized)
491 Controller.Settings.StartHidden.Value = true;
496 Controller.Settings.StartHidden.Value = false;
501 private void TsMenuCloseClick(object sender, EventArgs e)
506 private void CECTrayResize(object sender, EventArgs e)
508 if (WindowState == FormWindowState.Minimized)
514 private void TsMenuShowHideClick(object sender, EventArgs e)
519 public void ShowHideAdvanced(bool setTo)
523 tsAdvanced.Checked = true;
524 advancedModeToolStripMenuItem.Checked = true;
526 if (!tabPanel.Controls.Contains(tbTestCommands))
527 TabControls.Add(tbTestCommands);
528 if (!tabPanel.Controls.Contains(LogOutput))
529 TabControls.Add(LogOutput);
534 tsAdvanced.Checked = false;
535 advancedModeToolStripMenuItem.Checked = false;
537 tabPanel.Controls.Remove(tbTestCommands);
538 tabPanel.Controls.Remove(LogOutput);
543 private void TsAdvancedClick(object sender, EventArgs e)
545 Controller.Settings.AdvancedMode.Value = !tsAdvanced.Checked;
546 ShowHideAdvanced(!tsAdvanced.Checked);
549 public void SetStatusText(string status)
551 SetControlText(lStatus, status);
554 public void SetProgressBar(int progress, bool visible)
556 SetControlVisible(pProgress, visible);
557 SetProgressValue(pProgress, progress);
560 public void SetControlsEnabled(bool val)
563 SetControlEnabled(bClose, val);
564 SetControlEnabled(bSaveConfig, val);
565 SetControlEnabled(bReloadConfig, val);
568 SetControlEnabled(bRescanDevices, val);
569 SetControlEnabled(bSendImageViewOn, val);
570 SetControlEnabled(bStandby, val);
571 SetControlEnabled(bActivateSource, val);
572 SetControlEnabled(bScan, val);
574 bool enableVolumeButtons = (GetTargetDevice() == CecLogicalAddress.AudioSystem) && val;
575 SetControlEnabled(bVolUp, enableVolumeButtons);
576 SetControlEnabled(bVolDown, enableVolumeButtons);
577 SetControlEnabled(bMute, enableVolumeButtons);
580 private void TabControl1SelectedIndexChanged(object sender, EventArgs e)
582 switch (tabPanel.TabPages[tabPanel.SelectedIndex].Name)
584 case "tbTestCommands":
585 _selectedTab = ConfigTab.Tester;
588 _selectedTab = ConfigTab.Log;
592 _selectedTab = ConfigTab.Configuration;
598 #region Class members
599 private ConfigTab _selectedTab = ConfigTab.Configuration;
600 private string _log = string.Empty;
601 private CECController _controller;
602 public CECController Controller
606 return _controller ?? (_controller = new CECController(this));
609 public Control.ControlCollection TabControls
611 get { return tabPanel.Controls; }
613 public string SelectedTabName
615 get { return GetSelectedTabName(tabPanel, tabPanel.TabPages); }
618 private System.Windows.Forms.Timer _sstimer = new System.Windows.Forms.Timer();
621 private void AddNewApplicationToolStripMenuItemClick(object sender, EventArgs e)
623 ConfigureApplication appConfig = new ConfigureApplication(Controller.Settings, Controller);
624 Controller.DisplayDialog(appConfig, false);
629 /// The tab pages in this application
631 internal enum ConfigTab
641 class AsyncDisconnect
643 public AsyncDisconnect(CECController controller)
645 _controller = controller;
648 public void Process()
653 private CECController _controller;