michael@0: // screen.js: michael@0: // Set the screen size, pixel density and scaling of the b2g client screen michael@0: // based on the --screen command-line option, if there is one. michael@0: // michael@0: // TODO: support multiple device pixels per CSS pixel michael@0: // michael@0: michael@0: // We do this on ContentStart because querying the displayDPI fails otherwise. michael@0: window.addEventListener('ContentStart', function() { michael@0: // This is the toplevel element michael@0: let shell = document.getElementById('shell'); michael@0: michael@0: // The element inside it michael@0: let browser = document.getElementById('systemapp'); michael@0: michael@0: // Figure out the native resolution of the screen michael@0: let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor) michael@0: .getInterface(Components.interfaces.nsIDOMWindowUtils); michael@0: let hostDPI = windowUtils.displayDPI; michael@0: michael@0: let DEFAULT_SCREEN = '320x480'; michael@0: michael@0: // This is a somewhat random selection of named screens. michael@0: // Add more to this list when we support more hardware. michael@0: // Data from: http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density michael@0: let screens = { michael@0: iphone: { michael@0: name: 'Apple iPhone', width:320, height:480, dpi:163 michael@0: }, michael@0: ipad: { michael@0: name: 'Apple iPad', width:1024, height:768, dpi:132 michael@0: }, michael@0: nexus_s: { michael@0: name: 'Samsung Nexus S', width:480, height:800, dpi:235 michael@0: }, michael@0: galaxy_s2: { michael@0: name: 'Samsung Galaxy SII (I9100)', width:480, height:800, dpi:219 michael@0: }, michael@0: galaxy_nexus: { michael@0: name: 'Samsung Galaxy Nexus', width:720, height:1280, dpi:316 michael@0: }, michael@0: galaxy_tab: { michael@0: name: 'Samsung Galaxy Tab 10.1', width:800, height:1280, dpi:149 michael@0: }, michael@0: wildfire: { michael@0: name: 'HTC Wildfire', width:240, height:320, dpi:125 michael@0: }, michael@0: tattoo: { michael@0: name: 'HTC Tattoo', width:240, height:320, dpi:143 michael@0: }, michael@0: salsa: { michael@0: name: 'HTC Salsa', width:320, height:480, dpi:170 michael@0: }, michael@0: chacha: { michael@0: name: 'HTC ChaCha', width:320, height:480, dpi:222 michael@0: }, michael@0: }; michael@0: michael@0: // Get the command line arguments that were passed to the b2g client michael@0: let args; michael@0: try { michael@0: // On Firefox Mulet, we don't always have a command line argument michael@0: args = window.arguments[0].QueryInterface(Ci.nsICommandLine); michael@0: } catch(e) {} michael@0: michael@0: let screenarg = null; michael@0: michael@0: // Get the --screen argument from the command line michael@0: try { michael@0: if (args) { michael@0: screenarg = args.handleFlagWithParam('screen', false); michael@0: } michael@0: michael@0: // If there isn't one, use the default screen michael@0: if (screenarg === null) michael@0: screenarg = DEFAULT_SCREEN; michael@0: michael@0: // With no value, tell the user how to use it michael@0: if (screenarg == '') michael@0: usage(); michael@0: } michael@0: catch(e) { michael@0: // If getting the argument value fails, its an error michael@0: usage(); michael@0: } michael@0: michael@0: // Special case --screen=full goes into fullscreen mode michael@0: if (screenarg === 'full') { michael@0: shell.setAttribute('sizemode', 'fullscreen'); michael@0: return; michael@0: } michael@0: michael@0: let rescale = false; michael@0: michael@0: // If the value of --screen ends with !, we'll be scaling the output michael@0: if (screenarg[screenarg.length - 1] === '!') { michael@0: rescale = true; michael@0: screenarg = screenarg.substring(0, screenarg.length-1); michael@0: } michael@0: michael@0: let width, height, dpi; michael@0: michael@0: if (screenarg in screens) { michael@0: // If this is a named screen, get its data michael@0: let screen = screens[screenarg]; michael@0: width = screen.width; michael@0: height = screen.height; michael@0: dpi = screen.dpi; michael@0: } else { michael@0: // Otherwise, parse the resolution and density from the --screen value. michael@0: // The supported syntax is WIDTHxHEIGHT[@DPI] michael@0: let match = screenarg.match(/^(\d+)x(\d+)(@(\d+))?$/); michael@0: michael@0: // Display usage information on syntax errors michael@0: if (match == null) michael@0: usage(); michael@0: michael@0: // Convert strings to integers michael@0: width = parseInt(match[1], 10); michael@0: height = parseInt(match[2], 10); michael@0: if (match[4]) michael@0: dpi = parseInt(match[4], 10); michael@0: else // If no DPI, use the actual dpi of the host screen michael@0: dpi = hostDPI; michael@0: michael@0: // If any of the values came out 0 or NaN or undefined, display usage michael@0: if (!width || !height || !dpi) michael@0: usage(); michael@0: } michael@0: michael@0: Cu.import("resource://gre/modules/GlobalSimulatorScreen.jsm"); michael@0: function resize(width, height, dpi, shouldFlip) { michael@0: GlobalSimulatorScreen.width = width; michael@0: GlobalSimulatorScreen.height = height; michael@0: michael@0: // In order to do rescaling, we set the tag to the specified michael@0: // width and height, and then use a CSS transform to scale it so that michael@0: // it appears at the correct size on the host display. We also set michael@0: // the size of the element to that scaled target size. michael@0: let scale = rescale ? hostDPI / dpi : 1; michael@0: michael@0: // Set the window width and height to desired size plus chrome michael@0: // Include the size of the toolbox displayed under the system app michael@0: let controls = document.getElementById('controls'); michael@0: let controlsHeight = 0; michael@0: if (controls) { michael@0: controlsHeight = controls.getBoundingClientRect().height; michael@0: } michael@0: let chromewidth = window.outerWidth - window.innerWidth; michael@0: let chromeheight = window.outerHeight - window.innerHeight + controlsHeight; michael@0: window.resizeTo(Math.round(width * scale) + chromewidth, michael@0: Math.round(height * scale) + chromeheight); michael@0: michael@0: let frameWidth = width, frameHeight = height; michael@0: if (shouldFlip) { michael@0: frameWidth = height; michael@0: frameHeight = width; michael@0: } michael@0: michael@0: // Set the browser element to the full unscaled size of the screen michael@0: let style = browser.style; michael@0: style.width = style.minWidth = style.maxWidth = michael@0: frameWidth + 'px'; michael@0: style.height = style.minHeight = style.maxHeight = michael@0: frameHeight + 'px'; michael@0: browser.setAttribute('flex', '0'); // Don't let it stretch michael@0: michael@0: style.transformOrigin = ''; michael@0: style.transform = ''; michael@0: michael@0: // Now scale the browser element as needed michael@0: if (scale !== 1) { michael@0: style.transformOrigin = 'top left'; michael@0: style.transform += ' scale(' + scale + ',' + scale + ')'; michael@0: } michael@0: michael@0: if (shouldFlip) { michael@0: // Display the system app with a 90° clockwise rotation michael@0: let shift = Math.floor(Math.abs(frameWidth-frameHeight) / 2); michael@0: style.transform += michael@0: ' rotate(0.25turn) translate(-' + shift + 'px, -' + shift + 'px)'; michael@0: } michael@0: michael@0: // Set the pixel density that we want to simulate. michael@0: // This doesn't change the on-screen size, but makes michael@0: // CSS media queries and mozmm units work right. michael@0: Services.prefs.setIntPref('layout.css.dpi', dpi); michael@0: } michael@0: michael@0: // Resize on startup michael@0: resize(width, height, dpi, false); michael@0: michael@0: let defaultOrientation = width < height ? 'portrait' : 'landscape'; michael@0: michael@0: // Then resize on each rotation button click, michael@0: // or when the system app lock/unlock the orientation michael@0: Services.obs.addObserver(function orientationChangeListener(subject) { michael@0: let screen = subject.wrappedJSObject; michael@0: let { mozOrientation, screenOrientation } = screen; michael@0: michael@0: let newWidth = width; michael@0: let newHeight = height; michael@0: // If we have an orientation different than the startup one, michael@0: // we switch the sizes michael@0: if (screenOrientation != defaultOrientation) { michael@0: newWidth = height; michael@0: newHeight = width; michael@0: } michael@0: michael@0: // If the current app doesn't supports the current screen orientation michael@0: // still resize the window, but rotate its frame so that michael@0: // it is displayed rotated on the side michael@0: let shouldFlip = mozOrientation != screenOrientation; michael@0: michael@0: resize(newWidth, newHeight, dpi, shouldFlip); michael@0: }, 'simulator-adjust-window-size', false); michael@0: michael@0: // A utility function like console.log() for printing to the terminal window michael@0: // Uses dump(), but enables it first, if necessary michael@0: function print() { michael@0: let dump_enabled = michael@0: Services.prefs.getBoolPref('browser.dom.window.dump.enabled'); michael@0: michael@0: if (!dump_enabled) michael@0: Services.prefs.setBoolPref('browser.dom.window.dump.enabled', true); michael@0: michael@0: dump(Array.prototype.join.call(arguments, ' ') + '\n'); michael@0: michael@0: if (!dump_enabled) michael@0: Services.prefs.setBoolPref('browser.dom.window.dump.enabled', false); michael@0: } michael@0: michael@0: // Print usage info for --screen and exit michael@0: function usage() { michael@0: // Documentation for the --screen argument michael@0: let msg = michael@0: 'The --screen argument specifies the desired resolution and\n' + michael@0: 'pixel density of the simulated device screen. Use it like this:\n' + michael@0: '\t--screen=WIDTHxHEIGHT\t\t\t// E.g.: --screen=320x480\n' + michael@0: '\t--screen=WIDTHxHEIGHT@DOTS_PER_INCH\t// E.g.: --screen=480x800@250\n' + michael@0: '\t--screen=full\t\t\t\t// run in fullscreen mode\n' + michael@0: '\nYou can also specify certain device names:\n'; michael@0: for(let p in screens) michael@0: msg += '\t--screen=' + p + '\t// ' + screens[p].name + '\n'; michael@0: msg += michael@0: '\nAdd a ! to the end of a screen specification to rescale the\n' + michael@0: 'screen so that it is shown at actual size on your monitor:\n' + michael@0: '\t--screen=nexus_s!\n' + michael@0: '\t--screen=320x480@200!\n' michael@0: ; michael@0: michael@0: // Display the usage message michael@0: print(msg); michael@0: michael@0: // Exit the b2g client michael@0: Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit); michael@0: } michael@0: });