1
0
Fork 0
mirror of https://github.com/openstf/stf synced 2025-10-05 10:39:25 +02:00

Refactor screen code. Still not the best but a bit more manageable now.

This commit is contained in:
Simo Kinnunen 2014-12-10 19:48:04 +09:00
parent f9e2c4f0f2
commit 4a8806debc

View file

@ -1,7 +1,13 @@
var _ = require('lodash') var _ = require('lodash')
module.exports = function DeviceScreenDirective($document, ScalingService, module.exports = function DeviceScreenDirective(
VendorUtil, PageVisibilityService, BrowserInfo, $timeout) { $document
, ScalingService
, VendorUtil
, PageVisibilityService
, $timeout
, $window
) {
return { return {
restrict: 'E' restrict: 'E'
, template: require('./screen.jade') , template: require('./screen.jade')
@ -20,35 +26,47 @@ module.exports = function DeviceScreenDirective($document, ScalingService,
var input = element.find('input') var input = element.find('input')
var screen = scope.screen = {
rotation: 0
, bounds: {
x: 0
, y: 0
, w: 0
, h: 0
}
}
var scaler = ScalingService.coordinator(
device.display.width
, device.display.height
)
/** /**
* SCREEN HANDLING * SCREEN HANDLING
* *
* This section should deal with updating the screen ONLY. * This section should deal with updating the screen ONLY.
*/ */
;(function() { ;(function() {
var canvas = element.find('canvas')[0] var canvas = element.find('canvas')[0]
, g = canvas.getContext('2d') , g = canvas.getContext('2d')
var ws var devicePixelRatio = window.devicePixelRatio || 1
, loading = false , backingStoreRatio = g.webkitBackingStorePixelRatio ||
g.mozBackingStorePixelRatio ||
g.msBackingStorePixelRatio ||
g.oBackingStorePixelRatio ||
g.backingStorePixelRatio || 1
, frontBackRatio = devicePixelRatio / backingStoreRatio
var guestDisplayDensity = setDisplayDensity(1.5) var options = {
//var guestDisplayRotation = 0 autoScaleForRetina: true
, density: Math.max(1, Math.min(1.5, devicePixelRatio || 1))
var screen = scope.screen = { , minscale: 0.36
scaler: ScalingService.coordinator(
device.display.width, device.display.height
)
, rotation: 0
, bounds: {
x: 0
, y: 0
, w: 0
, h: 0
}
, autoScaleForRetina: true
} }
var updating = false
var cachedScreen = { var cachedScreen = {
rotation: 0 rotation: 0
, bounds: { , bounds: {
@ -62,24 +80,11 @@ module.exports = function DeviceScreenDirective($document, ScalingService,
var cachedImageWidth = 0 var cachedImageWidth = 0
, cachedImageHeight = 0 , cachedImageHeight = 0
// NOTE: instead of fa-pane-resize, a fa-child-pane-resize could be better
var onPanelResizeThrottled = _.throttle(updateBounds, 16)
scope.$on('fa-pane-resize', onPanelResizeThrottled)
function setDisplayDensity(forRetina) {
// FORCE
forRetina = 1.5
guestDisplayDensity = BrowserInfo.retina ? forRetina : 1
return guestDisplayDensity
}
function updateBounds() { function updateBounds() {
// TODO: element is an object HTMLUnknownElement in IE9
screen.bounds.w = element[0].offsetWidth screen.bounds.w = element[0].offsetWidth
screen.bounds.h = element[0].offsetHeight screen.bounds.h = element[0].offsetHeight
// TODO: element is an object HTMLUnknownElement in IE9
// Developer error, let's try to reduce debug time // Developer error, let's try to reduce debug time
if (!screen.bounds.w || !screen.bounds.h) { if (!screen.bounds.w || !screen.bounds.h) {
throw new Error( throw new Error(
@ -88,51 +93,41 @@ module.exports = function DeviceScreenDirective($document, ScalingService,
} }
} }
scope.retryLoadingScreen = function () {
if (scope.displayError === 'secure') {
control.home()
}
$timeout(maybeLoadScreen, 3000)
}
function maybeLoadScreen() { function maybeLoadScreen() {
var size var size
if (ws) { if (shouldUpdateScreen()) {
if (!loading && scope.$parent.showScreen && device) { switch (screen.rotation) {
switch (screen.rotation) { case 0:
case 0: case 180:
case 180: size = adjustBoundedSize(
size = adjustBoundedSize( screen.bounds.w
screen.bounds.w , screen.bounds.h
, screen.bounds.h )
) break
break case 90:
case 90: case 270:
case 270: size = adjustBoundedSize(
size = adjustBoundedSize( screen.bounds.h
screen.bounds.h , screen.bounds.w
, screen.bounds.w )
) break
break
}
loading = true
ws.send('{"op":"jpeg","w":' + size.w + ',"h":' + size.h + '}')
} }
updating = true
ws.send('{"op":"jpeg","w":' + size.w + ',"h":' + size.h + '}')
} }
} }
function adjustBoundedSize(w, h) { function adjustBoundedSize(w, h) {
var sw = w * guestDisplayDensity var sw = w * options.density
, sh = h * guestDisplayDensity , sh = h * options.density
, minscale = 0.36
, f , f
if (sw < (f = device.display.width * minscale)) { if (sw < (f = device.display.width * options.minscale)) {
sw *= f / sw sw *= f / sw
sh *= f / sh sh *= f / sh
} }
if (sh < (f = device.display.height * minscale)) { if (sh < (f = device.display.height * options.minscale)) {
sw *= f / sw sw *= f / sw
sh *= f / sh sh *= f / sh
} }
@ -143,152 +138,190 @@ module.exports = function DeviceScreenDirective($document, ScalingService,
} }
} }
function on() { function shouldUpdateScreen() {
if (!ws) { return (
ws = new WebSocket(device.display.url) // NO if we're updating already.
ws.binaryType = 'blob' !updating &&
} // NO if the user has disabled the screen.
scope.$parent.showScreen &&
ws.onerror = function() { // NO if we're not even using the device anymore.
// @todo HANDLE device.using &&
} // NO if the page invisible to the user?
!PageVisibilityService.hidden &&
ws.onclose = function() { // NO if we don't have a connection yet.
} ws.readyState === WebSocket.OPEN
// YES otherwise
ws.onopen = function() { )
maybeLoadScreen()
}
ws.onmessage = function(message) {
loading = false
if (scope.$parent.showScreen) {
screen.rotation = device.display.rotation
var blob = new Blob([message.data], {
type: 'image/jpeg'
})
var img = new Image()
img.onload = function() {
// Check to set the size only if updated
if (cachedScreen.bounds.w !== screen.bounds.w ||
cachedScreen.bounds.h !== screen.bounds.h ||
cachedImageWidth !== img.width ||
cachedImageHeight !== img.height ||
cachedScreen.rotation !== screen.rotation) {
cachedScreen.bounds.w = screen.bounds.w
cachedScreen.bounds.h = screen.bounds.h
cachedImageWidth = img.width
cachedImageHeight = img.height
cachedScreen.rotation = screen.rotation
canvas.width = cachedImageWidth
canvas.height = cachedImageHeight
var projectedSize = screen.scaler.projectedSize(
screen.bounds.w
, screen.bounds.h
, screen.rotation
)
canvas.style.width = projectedSize.width + 'px'
canvas.style.height = projectedSize.height + 'px'
// @todo Make sure that each position is able to rotate smoothly
// to the next one. This current setup doesn't work if rotation
// changes from 180 to 270 (it will do a reverse rotation).
switch (screen.rotation) {
case 0:
canvas.style[cssTransform] = 'rotate(0deg)'
break
case 90:
canvas.style[cssTransform] = 'rotate(-90deg)'
break
case 180:
canvas.style[cssTransform] = 'rotate(-180deg)'
break
case 270:
canvas.style[cssTransform] = 'rotate(90deg)'
break
}
}
g.drawImage(img, 0, 0)
// Try to forcefully clean everything to get rid of memory leaks.
img.onload = img.onerror = null
img.src = BLANK_IMG
img = null
url = null
blob = null
// Next please
maybeLoadScreen()
}
var url = URL.createObjectURL(blob)
img.src = url
// Reset error, if any
if (scope.displayError) {
scope.$apply(function () {
scope.displayError = false
})
}
}
}
updateBounds()
} }
function off() { function hasImageAreaChanged(img) {
loading = false return cachedScreen.bounds.w !== screen.bounds.w ||
cachedScreen.bounds.h !== screen.bounds.h ||
cachedImageWidth !== img.width ||
cachedImageHeight !== img.height ||
cachedScreen.rotation !== screen.rotation
}
if (ws) { function updateImageArea(img) {
ws.onmessage = ws.onerror = ws.onclose = null if (!hasImageAreaChanged(img)) {
return
}
cachedScreen.bounds.w = screen.bounds.w
cachedScreen.bounds.h = screen.bounds.h
cachedImageWidth = img.width
cachedImageHeight = img.height
cachedScreen.rotation = screen.rotation
if (options.autoScaleForRetina) {
canvas.width = cachedImageWidth * frontBackRatio
canvas.height = cachedImageHeight * frontBackRatio
g.scale(frontBackRatio, frontBackRatio)
}
else {
canvas.width = cachedImageWidth
canvas.height = cachedImageHeight
}
var projectedSize = scaler.projectedSize(
screen.bounds.w
, screen.bounds.h
, screen.rotation
)
canvas.style.width = projectedSize.width + 'px'
canvas.style.height = projectedSize.height + 'px'
// @todo Make sure that each position is able to rotate smoothly
// to the next one. This current setup doesn't work if rotation
// changes from 180 to 270 (it will do a reverse rotation).
switch (screen.rotation) {
case 0:
canvas.style[cssTransform] = 'rotate(0deg)'
break
case 90:
canvas.style[cssTransform] = 'rotate(-90deg)'
break
case 180:
canvas.style[cssTransform] = 'rotate(-180deg)'
break
case 270:
canvas.style[cssTransform] = 'rotate(90deg)'
break
}
}
function checkEnabled() {
if (shouldUpdateScreen()) {
updating = false
updateBounds()
maybeLoadScreen()
}
else {
g.clearRect(0, 0, canvas.width, canvas.height)
}
}
function stop() {
try {
ws.onerror = ws.onclose = ws.onmessage = ws.onopen = null
ws.close() ws.close()
ws = null ws = null
} }
catch (err) { /* noop */ }
} }
scope.$watch('$parent.showScreen', function (val) { var ws = new WebSocket(device.display.url)
if (val) { ws.binaryType = 'blob'
on()
} else {
off()
}
})
function checkEnabled() { ws.onerror = function errorListener() {
var using = device && device.using // @todo Handle
if (using && !PageVisibilityService.hidden) { }
on()
ws.onclose = function closeListener() {
// @todo Maybe handle
}
ws.onopen = function openListener() {
checkEnabled()
}
ws.onmessage = function messageListener(message) {
updating = false
if (shouldUpdateScreen()) {
screen.rotation = device.display.rotation
var blob = new Blob([message.data], {
type: 'image/jpeg'
})
var img = new Image()
img.onload = function() {
updateImageArea(this)
g.drawImage(img, 0, 0)
// Try to forcefully clean everything to get rid of memory leaks.
img.onload = img.onerror = null
img.src = BLANK_IMG
img = null
url = null
blob = null
}
img.onerror = function() {
// Happily ignore. I suppose this shouldn't happen, but
// sometimes it does, presumably when we're loading images
// too quickly.
}
var url = URL.createObjectURL(blob)
img.src = url
// Next please
maybeLoadScreen()
} }
else {
off() // Reset error, if any
if (scope.displayError) {
scope.$apply(function () {
scope.displayError = false
})
} }
} }
// NOTE: instead of fa-pane-resize, a fa-child-pane-resize could be better
scope.$on('fa-pane-resize', _.throttle(updateBounds, 16))
scope.retryLoadingScreen = function () {
if (scope.displayError === 'secure') {
control.home()
}
$timeout(maybeLoadScreen, 3000)
}
scope.$watch('device.using', checkEnabled) scope.$watch('device.using', checkEnabled)
scope.$on('visibilitychange', checkEnabled) scope.$on('visibilitychange', checkEnabled)
scope.$watch('$parent.showScreen', checkEnabled)
scope.$on('guest-portrait', function () { scope.$on('guest-portrait', function () {
control.rotate(0) control.rotate(0)
updateBounds()
}) })
scope.$on('guest-landscape', function () { scope.$on('guest-landscape', function () {
control.rotate(90) control.rotate(90)
setDisplayDensity(2)
updateBounds()
}) })
scope.$on('$destroy', off) $window.addEventListener('beforeunload', stop, false)
scope.$on('$destroy', function() {
stop()
$window.removeEventListener('beforeunload', stop, false)
})
})() })()
/** /**
@ -490,7 +523,7 @@ module.exports = function DeviceScreenDirective($document, ScalingService,
var x = e.pageX - screen.bounds.x var x = e.pageX - screen.bounds.x
, y = e.pageY - screen.bounds.y , y = e.pageY - screen.bounds.y
, pressure = 0.5 , pressure = 0.5
, scaled = screen.scaler.coords( , scaled = scaler.coords(
screen.bounds.w screen.bounds.w
, screen.bounds.h , screen.bounds.h
, x , x
@ -548,7 +581,7 @@ module.exports = function DeviceScreenDirective($document, ScalingService,
var x = e.pageX - screen.bounds.x var x = e.pageX - screen.bounds.x
, y = e.pageY - screen.bounds.y , y = e.pageY - screen.bounds.y
, pressure = 0.5 , pressure = 0.5
, scaled = screen.scaler.coords( , scaled = scaler.coords(
screen.bounds.w screen.bounds.w
, screen.bounds.h , screen.bounds.h
, x , x
@ -726,7 +759,7 @@ module.exports = function DeviceScreenDirective($document, ScalingService,
, x = touch.pageX - screen.bounds.x , x = touch.pageX - screen.bounds.x
, y = touch.pageY - screen.bounds.y , y = touch.pageY - screen.bounds.y
, pressure = touch.force || 0.5 , pressure = touch.force || 0.5
, scaled = screen.scaler.coords( , scaled = scaler.coords(
screen.bounds.w screen.bounds.w
, screen.bounds.h , screen.bounds.h
, x , x
@ -759,7 +792,7 @@ module.exports = function DeviceScreenDirective($document, ScalingService,
, x = touch.pageX - screen.bounds.x , x = touch.pageX - screen.bounds.x
, y = touch.pageY - screen.bounds.y , y = touch.pageY - screen.bounds.y
, pressure = touch.force || 0.5 , pressure = touch.force || 0.5
, scaled = screen.scaler.coords( , scaled = scaler.coords(
screen.bounds.w screen.bounds.w
, screen.bounds.h , screen.bounds.h
, x , x