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