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