listen to screensaver events in cectray. bugzid: 1375
[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
LOK
57
58 _sstimer.Interval = 1000;
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
115 _sstimer.Enabled = true;
116 OnSleep();
117 return;
118 }
119 else if (msg.Msg == WM_POWERBROADCAST)
fec04a82 120 {
07224bf3
LOK
121 switch (msg.WParam.ToInt32())
122 {
123 case PBT_APMSUSPEND:
124 OnSleep();
125 return;
126
127 case PBT_APMRESUMESUSPEND:
128 case PBT_APMRESUMECRITICAL:
129 case PBT_APMRESUMEAUTOMATIC:
130 OnWake();
131 return;
132
133 case PBT_POWERSETTINGCHANGE:
134 {
135 POWERBROADCAST_SETTING pwr = (POWERBROADCAST_SETTING)Marshal.PtrToStructure(msg.LParam, typeof(POWERBROADCAST_SETTING));
136 if (pwr.PowerSetting == GUID_SYSTEM_AWAYMODE && pwr.DataLength == Marshal.SizeOf(typeof(Int32)))
137 {
138 switch (pwr.Data)
139 {
140 case 0:
eb9465c9
LOK
141 // do _not_ wake the pc when away mode is deactivated
142 //OnWake();
143 //return;
07224bf3
LOK
144 case 1:
145 OnSleep();
146 return;
147 default:
148 break;
149 }
150 }
151 }
152 break;
153 default:
154 break;
155 }
fec04a82 156 }
07224bf3
LOK
157
158 // pass up when not handled
159 base.WndProc(ref msg);
160 }
161
b5bd4ac0
LOK
162 private void ScreensaverActiveCheck(object sender, EventArgs e)
163 {
164 if (!IsScreensaverActive())
165 {
166 _sstimer.Enabled = false;
167 OnWake();
168 }
169 }
170
171 private bool IsScreensaverActive()
172 {
173 int active = 1;
174 SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, ref active, 0);
175 return active == 1;
176 }
177
07224bf3
LOK
178 private void OnWake()
179 {
180 Controller.Initialise();
181 }
182
183 private void OnSleep()
184 {
185 Controller.Close();
f017f3c4
LOK
186 }
187
188 public override sealed string Text
189 {
190 get { return base.Text; }
191 set { base.Text = value; }
192 }
193
97401db1 194 public void Initialise()
f017f3c4 195 {
c4bc8944 196 Controller.Initialise();
f017f3c4
LOK
197 }
198
199 protected override void Dispose(bool disposing)
200 {
201 Hide();
202 if (disposing)
203 {
b9e6e386 204 Controller.CECActions.SuppressUpdates = true;
48b0a260
LOK
205 AsyncDisconnect dc = new AsyncDisconnect(Controller);
206 (new Thread(dc.Process)).Start();
f017f3c4
LOK
207 }
208 if (disposing && (components != null))
209 {
210 components.Dispose();
211 }
212 base.Dispose(disposing);
213 }
214
215 #region Configuration tab
216 /// <summary>
217 /// Replaces the gui controls by the ones that are bound to the settings.
218 /// 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
219 /// </summary>
220 public void InitialiseSettingsComponent(CECSettings settings)
221 {
222 settings.WakeDevices.ReplaceControls(this, Configuration.Controls, lWakeDevices, cbWakeDevices);
223 settings.PowerOffDevices.ReplaceControls(this, Configuration.Controls, lPowerOff, cbPowerOffDevices);
224 settings.OverridePhysicalAddress.ReplaceControls(this, Configuration.Controls, cbOverrideAddress);
225 settings.OverrideTVVendor.ReplaceControls(this, Configuration.Controls, cbVendorOverride);
226 settings.PhysicalAddress.ReplaceControls(this, Configuration.Controls, tbPhysicalAddress);
227 settings.HDMIPort.ReplaceControls(this, Configuration.Controls, lPortNumber, cbPortNumber);
228 settings.ConnectedDevice.ReplaceControls(this, Configuration.Controls, lConnectedDevice, cbConnectedDevice);
229 settings.ActivateSource.ReplaceControls(this, Configuration.Controls, cbActivateSource);
230 settings.DeviceType.ReplaceControls(this, Configuration.Controls, lDeviceType, cbDeviceType);
231 settings.TVVendor.ReplaceControls(this, Configuration.Controls, cbVendorId);
232 settings.StartHidden.ReplaceControls(this, Configuration.Controls, cbStartMinimised);
233 }
234
235 private void BSaveClick(object sender, EventArgs e)
236 {
c4bc8944 237 Controller.PersistSettings();
f017f3c4
LOK
238 }
239
240 private void BReloadConfigClick(object sender, EventArgs e)
241 {
c4bc8944 242 Controller.ResetDefaultSettings();
f017f3c4
LOK
243 }
244 #endregion
245
246 #region CEC Tester tab
247 delegate void SetActiveDevicesCallback(string[] activeDevices);
248 public void SetActiveDevices(string[] activeDevices)
249 {
250 if (cbCommandDestination.InvokeRequired)
251 {
252 SetActiveDevicesCallback d = SetActiveDevices;
253 try
254 {
255 Invoke(d, new object[] { activeDevices });
256 }
257 catch (Exception) { }
258 }
259 else
260 {
261 cbCommandDestination.Items.Clear();
262 foreach (string item in activeDevices)
263 cbCommandDestination.Items.Add(item);
264 }
265 }
266
267 delegate CecLogicalAddress GetTargetDeviceCallback();
268 private CecLogicalAddress GetTargetDevice()
269 {
270 if (cbCommandDestination.InvokeRequired)
271 {
272 GetTargetDeviceCallback d = GetTargetDevice;
273 CecLogicalAddress retval = CecLogicalAddress.Unknown;
274 try
275 {
276 retval = (CecLogicalAddress)Invoke(d, new object[] { });
277 }
278 catch (Exception) { }
279 return retval;
280 }
281
282 return CECSettingLogicalAddresses.GetLogicalAddressFromString(cbCommandDestination.Text);
283 }
284
285 private void BSendImageViewOnClick(object sender, EventArgs e)
286 {
c4bc8944 287 Controller.CECActions.SendImageViewOn(GetTargetDevice());
f017f3c4
LOK
288 }
289
290 private void BStandbyClick(object sender, EventArgs e)
291 {
c4bc8944 292 Controller.CECActions.SendStandby(GetTargetDevice());
f017f3c4
LOK
293 }
294
295 private void BScanClick(object sender, EventArgs e)
296 {
c4bc8944 297 Controller.CECActions.ShowDeviceInfo(GetTargetDevice());
f017f3c4
LOK
298 }
299
300 private void BActivateSourceClick(object sender, EventArgs e)
301 {
c4bc8944 302 Controller.CECActions.ActivateSource(GetTargetDevice());
f017f3c4
LOK
303 }
304
305 private void CbCommandDestinationSelectedIndexChanged(object sender, EventArgs e)
306 {
307 bool enableVolumeButtons = (GetTargetDevice() == CecLogicalAddress.AudioSystem);
308 bVolUp.Enabled = enableVolumeButtons;
309 bVolDown.Enabled = enableVolumeButtons;
310 bMute.Enabled = enableVolumeButtons;
311 bActivateSource.Enabled = (GetTargetDevice() != CecLogicalAddress.Broadcast);
312 bScan.Enabled = (GetTargetDevice() != CecLogicalAddress.Broadcast);
313 }
314
315 private void BVolUpClick(object sender, EventArgs e)
316 {
c4bc8944 317 Controller.Lib.VolumeUp(true);
f017f3c4
LOK
318 }
319
320 private void BVolDownClick(object sender, EventArgs e)
321 {
c4bc8944 322 Controller.Lib.VolumeDown(true);
f017f3c4
LOK
323 }
324
325 private void BMuteClick(object sender, EventArgs e)
326 {
c4bc8944 327 Controller.Lib.MuteAudio(true);
f017f3c4
LOK
328 }
329
330 private void BRescanDevicesClick(object sender, EventArgs e)
331 {
c4bc8944 332 Controller.CECActions.RescanDevices();
f017f3c4
LOK
333 }
334 #endregion
335
336 #region Log tab
337 delegate void UpdateLogCallback();
338 private void UpdateLog()
339 {
340 if (tbLog.InvokeRequired)
341 {
342 UpdateLogCallback d = UpdateLog;
343 try
344 {
345 Invoke(d, new object[] { });
346 }
347 catch (Exception) { }
348 }
349 else
350 {
351 tbLog.Text = _log;
352 tbLog.Select(tbLog.Text.Length, 0);
353 tbLog.ScrollToCaret();
354 }
355 }
356
357 public void AddLogMessage(CecLogMessage message)
358 {
359 string strLevel = "";
360 bool display = false;
361 switch (message.Level)
362 {
363 case CecLogLevel.Error:
364 strLevel = "ERROR: ";
365 display = cbLogError.Checked;
366 break;
367 case CecLogLevel.Warning:
368 strLevel = "WARNING: ";
369 display = cbLogWarning.Checked;
370 break;
371 case CecLogLevel.Notice:
372 strLevel = "NOTICE: ";
373 display = cbLogNotice.Checked;
374 break;
375 case CecLogLevel.Traffic:
376 strLevel = "TRAFFIC: ";
377 display = cbLogTraffic.Checked;
378 break;
379 case CecLogLevel.Debug:
380 strLevel = "DEBUG: ";
381 display = cbLogDebug.Checked;
382 break;
383 }
384
385 if (display)
386 {
387 string strLog = string.Format("{0} {1,16} {2}", strLevel, message.Time, message.Message) + Environment.NewLine;
388 AddLogMessage(strLog);
389 }
390 }
391
392 public void AddLogMessage(string message)
393 {
394 _log += message;
395
396 if (_selectedTab == ConfigTab.Log)
397 UpdateLog();
398 }
399
400 private void BClearLogClick(object sender, EventArgs e)
401 {
402 _log = string.Empty;
403 UpdateLog();
404 }
405
406 private void BSaveLogClick(object sender, EventArgs e)
407 {
408 SaveFileDialog dialog = new SaveFileDialog
409 {
410 Title = Resources.where_do_you_want_to_store_the_log,
411 InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
412 FileName = Resources.cec_log_filename,
413 Filter = Resources.cec_log_filter,
414 FilterIndex = 1
415 };
416
417 if (dialog.ShowDialog() == DialogResult.OK)
418 {
419 FileStream fs = (FileStream)dialog.OpenFile();
420 if (!fs.CanWrite)
421 {
422 MessageBox.Show(string.Format(Resources.cannot_open_for_writing, dialog.FileName), Resources.app_name, MessageBoxButtons.OK, MessageBoxIcon.Error);
423 }
424 else
425 {
426 StreamWriter writer = new StreamWriter(fs);
427 writer.Write(_log);
428 writer.Close();
429 fs.Close();
430 fs.Dispose();
431 MessageBox.Show(string.Format(Resources.log_stored_as, dialog.FileName), Resources.app_name, MessageBoxButtons.OK, MessageBoxIcon.Information);
432 }
433 }
434 }
435 #endregion
436
437 #region Tray icon and window controls
438 private void HideToolStripMenuItemClick(object sender, EventArgs e)
439 {
440 ShowHideToggle();
441 }
442
443 private void CloseToolStripMenuItemClick(object sender, EventArgs e)
444 {
445 Dispose();
446 }
447
448 private void AboutToolStripMenuItemClick(object sender, EventArgs e)
449 {
c4bc8944 450 (new About(Controller.LibServerVersion, Controller.LibClientVersion, Controller.LibInfo)).ShowDialog();
f017f3c4
LOK
451 }
452
453 private void AdvancedModeToolStripMenuItemClick(object sender, EventArgs e)
454 {
c4bc8944 455 Controller.Settings.AdvancedMode.Value = !advancedModeToolStripMenuItem.Checked;
f017f3c4
LOK
456 ShowHideAdvanced(!advancedModeToolStripMenuItem.Checked);
457 }
458
459 private void BCancelClick(object sender, EventArgs e)
460 {
461 Dispose();
462 }
463
464 private void TrayIconClick(object sender, EventArgs e)
465 {
466 if (e is MouseEventArgs && (e as MouseEventArgs).Button == MouseButtons.Left)
467 ShowHideToggle();
468 }
469
470 public void OnHide()
471 {
472 ShowInTaskbar = false;
473 Visible = false;
474 tsMenuShowHide.Text = Resources.show;
475 }
476
477 public void OnShow()
478 {
479 ShowInTaskbar = true;
480 WindowState = FormWindowState.Normal;
481 Activate();
482 tsMenuShowHide.Text = Resources.hide;
483 }
484
485 private void ShowHideToggle()
486 {
487 if (Visible && WindowState != FormWindowState.Minimized)
488 {
c4bc8944 489 Controller.Settings.StartHidden.Value = true;
f017f3c4
LOK
490 Hide();
491 }
492 else
493 {
c4bc8944 494 Controller.Settings.StartHidden.Value = false;
f017f3c4
LOK
495 Show();
496 }
497 }
498
499 private void TsMenuCloseClick(object sender, EventArgs e)
500 {
501 Dispose();
502 }
503
504 private void CECTrayResize(object sender, EventArgs e)
505 {
506 if (WindowState == FormWindowState.Minimized)
177e3a7b 507 Hide();
f017f3c4 508 else
177e3a7b 509 Show();
f017f3c4
LOK
510 }
511
512 private void TsMenuShowHideClick(object sender, EventArgs e)
513 {
514 ShowHideToggle();
515 }
516
517 public void ShowHideAdvanced(bool setTo)
518 {
519 if (setTo)
520 {
521 tsAdvanced.Checked = true;
522 advancedModeToolStripMenuItem.Checked = true;
523 SuspendLayout();
524 if (!tabPanel.Controls.Contains(tbTestCommands))
525 TabControls.Add(tbTestCommands);
526 if (!tabPanel.Controls.Contains(LogOutput))
527 TabControls.Add(LogOutput);
528 ResumeLayout();
529 }
530 else
531 {
532 tsAdvanced.Checked = false;
533 advancedModeToolStripMenuItem.Checked = false;
534 SuspendLayout();
535 tabPanel.Controls.Remove(tbTestCommands);
536 tabPanel.Controls.Remove(LogOutput);
537 ResumeLayout();
538 }
539 }
540
541 private void TsAdvancedClick(object sender, EventArgs e)
542 {
c4bc8944 543 Controller.Settings.AdvancedMode.Value = !tsAdvanced.Checked;
f017f3c4
LOK
544 ShowHideAdvanced(!tsAdvanced.Checked);
545 }
546
547 public void SetStatusText(string status)
548 {
549 SetControlText(lStatus, status);
550 }
551
552 public void SetProgressBar(int progress, bool visible)
553 {
554 SetControlVisible(pProgress, visible);
555 SetProgressValue(pProgress, progress);
556 }
557
558 public void SetControlsEnabled(bool val)
559 {
560 //main tab
561 SetControlEnabled(bClose, val);
562 SetControlEnabled(bSaveConfig, val);
563 SetControlEnabled(bReloadConfig, val);
564
565 //tester tab
566 SetControlEnabled(bRescanDevices, val);
567 SetControlEnabled(bSendImageViewOn, val);
568 SetControlEnabled(bStandby, val);
569 SetControlEnabled(bActivateSource, val);
570 SetControlEnabled(bScan, val);
571
572 bool enableVolumeButtons = (GetTargetDevice() == CecLogicalAddress.AudioSystem) && val;
573 SetControlEnabled(bVolUp, enableVolumeButtons);
574 SetControlEnabled(bVolDown, enableVolumeButtons);
575 SetControlEnabled(bMute, enableVolumeButtons);
576 }
577
578 private void TabControl1SelectedIndexChanged(object sender, EventArgs e)
579 {
580 switch (tabPanel.TabPages[tabPanel.SelectedIndex].Name)
581 {
582 case "tbTestCommands":
583 _selectedTab = ConfigTab.Tester;
584 break;
585 case "LogOutput":
586 _selectedTab = ConfigTab.Log;
587 UpdateLog();
588 break;
589 default:
590 _selectedTab = ConfigTab.Configuration;
591 break;
592 }
593 }
594 #endregion
595
596 #region Class members
597 private ConfigTab _selectedTab = ConfigTab.Configuration;
598 private string _log = string.Empty;
c4bc8944 599 private CECController _controller;
49689754
LOK
600 public CECController Controller
601 {
97401db1
LOK
602 get
603 {
604 return _controller ?? (_controller = new CECController(this));
605 }
49689754 606 }
f017f3c4
LOK
607 public Control.ControlCollection TabControls
608 {
609 get { return tabPanel.Controls; }
610 }
611 public string SelectedTabName
612 {
613 get { return GetSelectedTabName(tabPanel, tabPanel.TabPages); }
614 }
b5bd4ac0
LOK
615
616 private System.Windows.Forms.Timer _sstimer = new System.Windows.Forms.Timer();
f017f3c4
LOK
617 #endregion
618
619 private void AddNewApplicationToolStripMenuItemClick(object sender, EventArgs e)
620 {
c4bc8944
LOK
621 ConfigureApplication appConfig = new ConfigureApplication(Controller.Settings, Controller);
622 Controller.DisplayDialog(appConfig, false);
f017f3c4
LOK
623 }
624 }
a1b14f2e
LOK
625
626 /// <summary>
627 /// The tab pages in this application
628 /// </summary>
629 internal enum ConfigTab
630 {
631 Configuration,
632 KeyConfiguration,
633 Tester,
634 Log,
635 WMC,
636 XBMC
637 }
638
639 class AsyncDisconnect
640 {
641 public AsyncDisconnect(CECController controller)
642 {
643 _controller = controller;
644 }
645
646 public void Process()
647 {
648 _controller.Close();
649 }
650
651 private CECController _controller;
652 }
f017f3c4 653}