sbOSDControlService.js
Go to the documentation of this file.
1 /*
2  *=BEGIN SONGBIRD GPL
3  *
4  * This file is part of the Songbird web player.
5  *
6  * Copyright(c) 2005-2010 POTI, Inc.
7  * http://www.songbirdnest.com
8  *
9  * This file may be licensed under the terms of of the
10  * GNU General Public License Version 2 (the ``GPL'').
11  *
12  * Software distributed under the License is distributed
13  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
14  * express or implied. See the GPL for the specific language
15  * governing rights and limitations.
16  *
17  * You should have received a copy of the GPL along with this
18  * program. If not, go to http://www.gnu.org/licenses/gpl.html
19  * or write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  *=END SONGBIRD GPL
23  */
24 
25 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
26 
27 if (typeof(Cc) == "undefined")
28  var Cc = Components.classes;
29 if (typeof(Ci) == "undefined")
30  var Ci = Components.interfaces;
31 if (typeof(Cu) == "undefined")
32  var Cu = Components.utils;
33 if (typeof(Cr) == "undefined")
34  var Cr = Components.results;
35 
36 
37 //------------------------------------------------------------------------------
38 // Constants
39 
40 const SB_OSDHIDE_DELAY = 3000;
41 const MAX_OSD_WIDTH = 502;
42 const OSD_HEIGHT = 70;
43 const OSD_PADDING = 18; // 9 on each side.
44 
45 
46 //==============================================================================
47 //
48 // @interface sbOSDControlService
49 // @brief Service to provide on-screen-display controls for video playback.
50 //
51 //==============================================================================
52 
54 {
55  this._cloakService = Cc["@songbirdnest.com/Songbird/WindowCloak;1"]
56  .getService(Ci.sbIWindowCloak);
57  this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
58 
59  // Ask the window chrome service (if available) if it supports window
60  // composition to enable transparent graphics for the OSD.
61  this._useTransparentGraphics = true;
62  if ("@songbirdnest.com/Songbird/WindowChromeService;1" in Cc) {
63  var winChromeService =
64  Cc["@songbirdnest.com/Songbird/WindowChromeService;1"]
65  .getService(Ci.sbIWindowChromeService);
66  this._useTransparentGraphics = winChromeService.isCompositionEnabled;
67  }
68 }
69 
71 {
72  _videoWindow: null,
73  _osdWindow: null,
74  _cloakService: null,
75  _timer: null,
76  _osdControlsShowing: false,
77  _useTransparentGraphics: false,
78 
79  _videoWinHasFocus: false,
80  _osdWinHasFocus: false,
81  _mouseDownOnOSD: false,
82 
83  _fadeInterval: null,
84  _fadeContinuation: null,
85 
86 
87  _recalcOSDPosition: function() {
88  // Compute the position of the OSD controls using the video windows local
89  // and screen coordinates.
90  var osdY = this._videoWindow.screenY +
91  (this._videoWindow.outerHeight * 0.95 - this._osdWindow.outerHeight);
92 
93  // Resize the osd window as needed. (after move if needed)
94  var newOSDWidth = this._osdWindow.innerWidth;
95  if (this._videoWindow.innerWidth + OSD_PADDING > MAX_OSD_WIDTH) {
96  newOSDWidth = MAX_OSD_WIDTH;
97  }
98  else {
99  newOSDWidth = this._videoWindow.innerWidth - OSD_PADDING;
100  }
101 
102  // Now compute X position.
103  var osdX = this._videoWindow.screenX +
104  (this._videoWindow.innerWidth / 2) - (newOSDWidth / 2);
105 
106  // Move to the new (x,y) coordinates.
107  this._osdWindow.moveTo(osdX, osdY);
108 
109  // Now finally resize the osd window.
110  this._osdWindow.resizeTo(newOSDWidth, OSD_HEIGHT);
111  },
112 
113  //----------------------------------------------------------------------------
114  // sbIOSDControlService
115 
116  onVideoWindowOpened: function(aVideoWindow) {
117  // For now, just open the OSD control pane thingy.
118  this._videoWindow = aVideoWindow.QueryInterface(Ci.nsIDOMWindowInternal);
119 
120  // Create a OSD overlay window.
121  this._osdWindow = this._videoWindow.openDialog(
122  "chrome://songbird/content/xul/videoWindowControls.xul",
123  "Songbird OSD Control Window",
124  "chrome,dependent,modal=no,titlebar=no",
125  null);
126  this._osdWindow.QueryInterface(Ci.nsIDOMWindowInternal);
127 
128  // Cloak the window right now.
129  this._cloakService.cloak(this._osdWindow);
130 
131  try {
132  // Not all platforms have this service.
133  var winMoveService =
134  Cc["@songbirdnest.com/integration/window-move-resize-service;1"]
135  .getService(Ci.sbIWindowMoveService);
136 
137  winMoveService.startWatchingWindow(this._videoWindow, this);
138  }
139  catch (e) {
140  // No window move service on this platform.
141  }
142 
143  // Listen for blur and focus events for determing when both the video
144  // window and the OSD controls loose focus.
145  var self = this;
146  this._osdWinBlurListener = function(aEvent) {
147  self._onOSDWinBlur(aEvent);
148  };
149  this._videoWinBlurListener = function(aEvent) {
150  self._onVideoWinBlur(aEvent);
151  };
152  this._osdWinFocusListener = function(aEvent) {
153  self._onOSDWinFocus(aEvent);
154  };
155  this._videoWinFocusListener = function(aEvent) {
156  self._onVideoWinFocus(aEvent);
157  };
158  this._osdWinMousemoveListener = function(aEvent) {
159  self._onOSDWinMousemove(aEvent);
160  };
161  this._osdWinMousedownListener = function(aEvent) {
162  self._onOSDWinMousedown(aEvent);
163  };
164  this._osdWinMouseupListener = function(aEvent) {
165  self._onOSDWinMouseup(aEvent);
166  };
167  this._osdWinKeypressListener = function(aEvent) {
168  self._onOSDWinKeypress(aEvent);
169  };
170  this._osdWindow.addEventListener("blur",
171  this._osdWinBlurListener,
172  false);
173  this._osdWindow.addEventListener("focus",
174  this._osdWinFocusListener,
175  false);
176  this._osdWindow.addEventListener("mousemove",
177  this._osdWinMousemoveListener,
178  false);
179  this._videoWindow.addEventListener("mousemove",
180  this._osdWinMousemoveListener,
181  false);
182  this._osdWindow.addEventListener("mousedown",
183  this._osdWinMousedownListener,
184  false);
185  this._osdWindow.addEventListener("mouseup",
186  this._osdWinMouseupListener,
187  false);
188  this._osdWindow.addEventListener("keypress",
189  this._osdWinKeypressListener,
190  false);
191  this._videoWindow.addEventListener("blur",
192  this._videoWinBlurListener,
193  false);
194  this._videoWindow.addEventListener("focus",
195  this._videoWinFocusListener,
196  false);
197  },
198 
199  onVideoWindowWillClose: function() {
200  try {
201  // Not all platforms have this service.
202  var winMoveService =
203  Cc["@songbirdnest.com/integration/window-move-resize-service;1"]
204  .getService(Ci.sbIWindowMoveService);
205 
206  winMoveService.stopWatchingWindow(this._videoWindow, this);
207  }
208  catch (e) {
209  // No window move service on this platform.
210  }
211 
212  this._timer.cancel();
213  this._osdWindow.removeEventListener("blur",
214  this._osdWinBlurListener,
215  false);
216  this._osdWindow.removeEventListener("focus",
217  this._osdWinFocusListener,
218  false);
219  this._osdWindow.removeEventListener("mousemove",
220  this._osdWinMousemoveListener,
221  false);
222  this._osdWindow.removeEventListener("mousedown",
223  this._osdWinMousedownListener,
224  false);
225  this._osdWindow.removeEventListener("mouseup",
226  this._osdWinMouseupListener,
227  false);
228  this._osdWindow.removeEventListener("keypress",
229  this._osdWinKeypressListener,
230  false);
231  this._videoWindow.removeEventListener("blur",
232  this._videoWinBlurListener,
233  false);
234  this._videoWindow.removeEventListener("focus",
235  this._videoWinFocusListener,
236  false);
237  this._osdWindow.close();
238  this._osdWindow = null;
239  this._videoWindow = null;
240  },
241 
242  onVideoWindowResized: function() {
243  this._recalcOSDPosition();
244  },
245 
246  onVideoWindowFullscreenChanged: function(aFullscreen) {
247  var outterBox =
248  this._osdWindow.document.getElementById("osd_wrapper_hbox");
249  var fullscreenButton =
250  this._osdWindow.document.getElementById("full_screen_button");
251 
252  if(outterBox) {
253  if(aFullscreen) {
254  outterBox.setAttribute("fullscreen", true);
255  fullscreenButton.setAttribute("fullscreen", true);
256  }
257  else {
258  outterBox.removeAttribute("fullscreen");
259  fullscreenButton.removeAttribute("fullscreen");
260  }
261  }
262  },
263 
264  hideOSDControls: function(aTransitionType) {
265  var self = this;
266  // Don't hide the window while the user is dragging
267  if (this._mouseDownOnOSD)
268  return;
269 
270  this._timer.cancel();
271 
272  var transition;
273  switch (aTransitionType) {
274  case Ci.sbIOSDControlService.TRANSITION_FADE:
275  transition = this._fadeOut;
276  break;
277 
278  case Ci.sbIOSDControlService.TRANSITION_NONE:
279  transition = this._hideInstantly;
280  break;
281 
282  default:
283  Components.utils.reportError(
284  "Invalid transition type passed into hideOSDControls()!");
285 
286  // Just fall back to hiding instantly.
287  transition = this._hideInstantly;
288  }
289 
290  if (!this._useTransparentGraphics) {
291  transition = this._hideInstantly;
292  }
293 
294  transition.call(this, function() {
295  // The OSD controls are no longer showing. The order of events
296  // here is critical; surprisingly, the cloaking must happen
297  // last.
298  self._osdControlsShowing = false;
299 
300  if (!self._cloakService.isCloaked(self._osdWindow)) {
301  if (self._osdWinHasFocus) {
302  self._videoWindow.focus();
303  }
304  self._cloakService.cloak(self._osdWindow);
305  }
306  });
307  },
308 
309  showOSDControls: function(aTransitionType) {
310  if (!this._videoWinHasFocus &&
311  !this._osdWinHasFocus)
312  {
313  // Don't bother showing the controls if the video and the OSD window have
314  // lost focus. This prevents floating the OSD controls ontop of every
315  // other window in the OS.
316  return;
317  }
318 
319  if (this._osdSurpressed)
320  return;
321 
322  this._timer.cancel();
323  this._timer.initWithCallback(this,
325  Ci.nsITimer.TYPE_ONE_SHOT);
326 
327  // if the osd is already visible, then all we need to do is reset the timer
328  // and make sure the controls are focused.
329  if (this._osdControlsShowing) {
330  this._osdWindow.focus();
331  return;
332  }
333 
334  // Controls are showing
335  this._osdControlsShowing = true;
336  this._recalcOSDPosition();
337 
338  // Show the controls if they are currently hidden.
339  if (this._cloakService.isCloaked(this._osdWindow)) {
340  this._cloakService.uncloak(this._osdWindow);
341  }
342 
343  var transition;
344  switch (aTransitionType) {
345  case Ci.sbIOSDControlService.TRANSITION_FADE:
346  transition = this._fadeIn;
347  break;
348 
349  case Ci.sbIOSDControlService.TRANSITION_NONE:
350  transition = this._showInstantly;
351  break;
352 
353  default:
354  Components.utils.reportError(
355  "Invalid transition type passed into showOSDControls()!");
356 
357  // Just fall back to show instantly.
358  transition = this._showInstantly;
359  }
360 
361  if (!this._useTransparentGraphics) {
362  transition = this._showInstantly;
363  }
364 
365  transition.call(this);
366  },
367 
368  _fade: function(start, end, func) {
369  var self = this;
370  this._fadeCancel();
371  this._fadeContinuation = func;
372  var node = this._osdWindow.document.getElementById("osd_wrapper_hbox");
373  var opacity = start;
374  var delta = (end - start) / 10;
375  var step = 1;
376 
377  self._fadeInterval = self._osdWindow.setInterval(function() {
378  opacity += delta;
379  node.style.opacity = opacity;
380 
381  if (step++ >= 9) {
382  self._fadeCancel();
383  node = null;
384  }
385  }, 50);
386  },
387 
388  _fadeCancel: function() {
389  this._osdWindow.clearInterval(this._fadeInterval);
390  if (this._fadeContinuation) {
391  this._fadeContinuation();
392  }
393  this._fadeContinuation = null;
394  },
395 
396  _showInstantly: function(func) {
397  this._fadeCancel();
398  var node = this._osdWindow.document.getElementById("osd_wrapper_hbox");
399  if (node) {
400  node.style.opacity = 1;
401  }
402  if (func) {
403  func();
404  }
405  },
406 
407  _hideInstantly: function(func) {
408  this._fadeCancel();
409  if (func) {
410  func();
411  }
412  },
413 
414  // fade from 100% opaque to 0% opaque
415  _fadeOut: function(func) {
416  this._fade(1, 0, func);
417  },
418 
419  // fade from 0% opaque to 100% opaque
420  _fadeIn: function(func) {
421  this._fade(0, 1, func);
422  },
423 
424  _onOSDWinBlur: function(aEvent) {
425  this._osdWinHasFocus = false;
426  },
427 
428  _onOSDWinFocus: function(aEvent) {
429  this._osdWinHasFocus = true;
430  },
431 
432  _onVideoWinBlur: function(aEvent) {
433  this._videoWinHasFocus = false;
434  },
435 
436  _onVideoWinFocus: function(aEvent) {
437  this._videoWinHasFocus = true;
438  },
439 
440  _onOSDWinMousemove: function(aEvent) {
441  // The user has the mouse over the OSD controls, ensure that the controls
442  // are visible.
443  this.showOSDControls(Ci.sbIOSDControlService.TRANSITION_NONE);
444  },
445 
446  _onOSDWinMousedown: function(aEvent) {
447  if (aEvent.button == 0) {
448  this._mouseDownOnOSD = true;
449  }
450  },
451 
452  _onOSDWinMouseup: function(aEvent) {
453  if (aEvent.button == 0) {
454  // User released the mouse button, reset the timer
455  this._mouseDownOnOSD = false;
456  this.showOSDControls(Ci.sbIOSDControlService.TRANSITION_NONE);
457  }
458  },
459 
460  _onOSDWinKeypress: function(aEvent) {
461  if (!aEvent.getPreventDefault())
462  {
463  let event = this._videoWindow.document.createEvent("KeyboardEvent");
464  event.initKeyEvent(aEvent.type, aEvent.bubbles, aEvent.cancelable,
465  this._videoWindow, aEvent.ctrlKey, aEvent.altKey,
466  aEvent.shiftKey, aEvent.metaKey, aEvent.keyCode,
467  aEvent.charCode);
468  this._videoWindow.dispatchEvent(event);
469  }
470  },
471 
472  //----------------------------------------------------------------------------
473  // sbIWindowMoveListener
474 
475  onMoveStarted: function() {
476  this._osdSurpressed = true;
477  this._showOSDControlsOnStop = this._osdControlsShowing;
478  this.hideOSDControls(Ci.sbIOSDControlService.TRANSITION_NONE);
479  },
480 
481  onMoveStopped: function() {
482  this._osdSurpressed = false;
483  if (this._showOSDControlsOnStop) {
484  this.showOSDControls(Ci.sbIOSDControlService.TRANSITION_NONE);
485  }
486  this._showOSDControlsOnStop = false;
487  },
488 
489  //----------------------------------------------------------------------------
490  // nsITimerCallback
491 
492  notify: function(aTimer) {
493  if (aTimer == this._timer) {
494  this.hideOSDControls(Ci.sbIOSDControlService.TRANSITION_FADE);
495  }
496  },
497 };
498 
499 //------------------------------------------------------------------------------
500 // XPCOM Registration
501 
502 sbOSDControlService.prototype.classDescription =
503  "Songbird OSD Control Service";
504 sbOSDControlService.prototype.className =
505  "sbOSDControlService";
506 sbOSDControlService.prototype.classID =
507  Components.ID("{03F78779-FCB7-4442-9A0C-E8547B4F1368}");
508 sbOSDControlService.prototype.contractID =
509  "@songbirdnest.com/mediacore/osd-control-service;1";
510 sbOSDControlService.prototype.QueryInterface =
511  XPCOMUtils.generateQI([Ci.sbIOSDControlService,
512  Ci.sbIWindowMoveListener,
513  Ci.nsITimerCallback]);
514 
515 function NSGetModule(compMgr, fileSpec)
516 {
517  return XPCOMUtils.generateModule([sbOSDControlService]);
518 }
519 
function start(ch)
const Cu
function sbOSDControlService()
const Cc
function NSGetModule(compMgr, fileSpec)
var event
const OSD_HEIGHT
const SB_OSDHIDE_DELAY
sbDeviceFirmwareAutoCheckForUpdate prototype _timer
Element Properties opacity
TimerLoop prototype notify
const MAX_OSD_WIDTH
return null
Definition: FeedWriter.js:1143
let node
const Cr
const Ci
const OSD_PADDING