1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/b2g/chrome/content/screen.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,258 @@ 1.4 +// screen.js: 1.5 +// Set the screen size, pixel density and scaling of the b2g client screen 1.6 +// based on the --screen command-line option, if there is one. 1.7 +// 1.8 +// TODO: support multiple device pixels per CSS pixel 1.9 +// 1.10 + 1.11 +// We do this on ContentStart because querying the displayDPI fails otherwise. 1.12 +window.addEventListener('ContentStart', function() { 1.13 + // This is the toplevel <window> element 1.14 + let shell = document.getElementById('shell'); 1.15 + 1.16 + // The <browser> element inside it 1.17 + let browser = document.getElementById('systemapp'); 1.18 + 1.19 + // Figure out the native resolution of the screen 1.20 + let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor) 1.21 + .getInterface(Components.interfaces.nsIDOMWindowUtils); 1.22 + let hostDPI = windowUtils.displayDPI; 1.23 + 1.24 + let DEFAULT_SCREEN = '320x480'; 1.25 + 1.26 + // This is a somewhat random selection of named screens. 1.27 + // Add more to this list when we support more hardware. 1.28 + // Data from: http://en.wikipedia.org/wiki/List_of_displays_by_pixel_density 1.29 + let screens = { 1.30 + iphone: { 1.31 + name: 'Apple iPhone', width:320, height:480, dpi:163 1.32 + }, 1.33 + ipad: { 1.34 + name: 'Apple iPad', width:1024, height:768, dpi:132 1.35 + }, 1.36 + nexus_s: { 1.37 + name: 'Samsung Nexus S', width:480, height:800, dpi:235 1.38 + }, 1.39 + galaxy_s2: { 1.40 + name: 'Samsung Galaxy SII (I9100)', width:480, height:800, dpi:219 1.41 + }, 1.42 + galaxy_nexus: { 1.43 + name: 'Samsung Galaxy Nexus', width:720, height:1280, dpi:316 1.44 + }, 1.45 + galaxy_tab: { 1.46 + name: 'Samsung Galaxy Tab 10.1', width:800, height:1280, dpi:149 1.47 + }, 1.48 + wildfire: { 1.49 + name: 'HTC Wildfire', width:240, height:320, dpi:125 1.50 + }, 1.51 + tattoo: { 1.52 + name: 'HTC Tattoo', width:240, height:320, dpi:143 1.53 + }, 1.54 + salsa: { 1.55 + name: 'HTC Salsa', width:320, height:480, dpi:170 1.56 + }, 1.57 + chacha: { 1.58 + name: 'HTC ChaCha', width:320, height:480, dpi:222 1.59 + }, 1.60 + }; 1.61 + 1.62 + // Get the command line arguments that were passed to the b2g client 1.63 + let args; 1.64 + try { 1.65 + // On Firefox Mulet, we don't always have a command line argument 1.66 + args = window.arguments[0].QueryInterface(Ci.nsICommandLine); 1.67 + } catch(e) {} 1.68 + 1.69 + let screenarg = null; 1.70 + 1.71 + // Get the --screen argument from the command line 1.72 + try { 1.73 + if (args) { 1.74 + screenarg = args.handleFlagWithParam('screen', false); 1.75 + } 1.76 + 1.77 + // If there isn't one, use the default screen 1.78 + if (screenarg === null) 1.79 + screenarg = DEFAULT_SCREEN; 1.80 + 1.81 + // With no value, tell the user how to use it 1.82 + if (screenarg == '') 1.83 + usage(); 1.84 + } 1.85 + catch(e) { 1.86 + // If getting the argument value fails, its an error 1.87 + usage(); 1.88 + } 1.89 + 1.90 + // Special case --screen=full goes into fullscreen mode 1.91 + if (screenarg === 'full') { 1.92 + shell.setAttribute('sizemode', 'fullscreen'); 1.93 + return; 1.94 + } 1.95 + 1.96 + let rescale = false; 1.97 + 1.98 + // If the value of --screen ends with !, we'll be scaling the output 1.99 + if (screenarg[screenarg.length - 1] === '!') { 1.100 + rescale = true; 1.101 + screenarg = screenarg.substring(0, screenarg.length-1); 1.102 + } 1.103 + 1.104 + let width, height, dpi; 1.105 + 1.106 + if (screenarg in screens) { 1.107 + // If this is a named screen, get its data 1.108 + let screen = screens[screenarg]; 1.109 + width = screen.width; 1.110 + height = screen.height; 1.111 + dpi = screen.dpi; 1.112 + } else { 1.113 + // Otherwise, parse the resolution and density from the --screen value. 1.114 + // The supported syntax is WIDTHxHEIGHT[@DPI] 1.115 + let match = screenarg.match(/^(\d+)x(\d+)(@(\d+))?$/); 1.116 + 1.117 + // Display usage information on syntax errors 1.118 + if (match == null) 1.119 + usage(); 1.120 + 1.121 + // Convert strings to integers 1.122 + width = parseInt(match[1], 10); 1.123 + height = parseInt(match[2], 10); 1.124 + if (match[4]) 1.125 + dpi = parseInt(match[4], 10); 1.126 + else // If no DPI, use the actual dpi of the host screen 1.127 + dpi = hostDPI; 1.128 + 1.129 + // If any of the values came out 0 or NaN or undefined, display usage 1.130 + if (!width || !height || !dpi) 1.131 + usage(); 1.132 + } 1.133 + 1.134 + Cu.import("resource://gre/modules/GlobalSimulatorScreen.jsm"); 1.135 + function resize(width, height, dpi, shouldFlip) { 1.136 + GlobalSimulatorScreen.width = width; 1.137 + GlobalSimulatorScreen.height = height; 1.138 + 1.139 + // In order to do rescaling, we set the <browser> tag to the specified 1.140 + // width and height, and then use a CSS transform to scale it so that 1.141 + // it appears at the correct size on the host display. We also set 1.142 + // the size of the <window> element to that scaled target size. 1.143 + let scale = rescale ? hostDPI / dpi : 1; 1.144 + 1.145 + // Set the window width and height to desired size plus chrome 1.146 + // Include the size of the toolbox displayed under the system app 1.147 + let controls = document.getElementById('controls'); 1.148 + let controlsHeight = 0; 1.149 + if (controls) { 1.150 + controlsHeight = controls.getBoundingClientRect().height; 1.151 + } 1.152 + let chromewidth = window.outerWidth - window.innerWidth; 1.153 + let chromeheight = window.outerHeight - window.innerHeight + controlsHeight; 1.154 + window.resizeTo(Math.round(width * scale) + chromewidth, 1.155 + Math.round(height * scale) + chromeheight); 1.156 + 1.157 + let frameWidth = width, frameHeight = height; 1.158 + if (shouldFlip) { 1.159 + frameWidth = height; 1.160 + frameHeight = width; 1.161 + } 1.162 + 1.163 + // Set the browser element to the full unscaled size of the screen 1.164 + let style = browser.style; 1.165 + style.width = style.minWidth = style.maxWidth = 1.166 + frameWidth + 'px'; 1.167 + style.height = style.minHeight = style.maxHeight = 1.168 + frameHeight + 'px'; 1.169 + browser.setAttribute('flex', '0'); // Don't let it stretch 1.170 + 1.171 + style.transformOrigin = ''; 1.172 + style.transform = ''; 1.173 + 1.174 + // Now scale the browser element as needed 1.175 + if (scale !== 1) { 1.176 + style.transformOrigin = 'top left'; 1.177 + style.transform += ' scale(' + scale + ',' + scale + ')'; 1.178 + } 1.179 + 1.180 + if (shouldFlip) { 1.181 + // Display the system app with a 90° clockwise rotation 1.182 + let shift = Math.floor(Math.abs(frameWidth-frameHeight) / 2); 1.183 + style.transform += 1.184 + ' rotate(0.25turn) translate(-' + shift + 'px, -' + shift + 'px)'; 1.185 + } 1.186 + 1.187 + // Set the pixel density that we want to simulate. 1.188 + // This doesn't change the on-screen size, but makes 1.189 + // CSS media queries and mozmm units work right. 1.190 + Services.prefs.setIntPref('layout.css.dpi', dpi); 1.191 + } 1.192 + 1.193 + // Resize on startup 1.194 + resize(width, height, dpi, false); 1.195 + 1.196 + let defaultOrientation = width < height ? 'portrait' : 'landscape'; 1.197 + 1.198 + // Then resize on each rotation button click, 1.199 + // or when the system app lock/unlock the orientation 1.200 + Services.obs.addObserver(function orientationChangeListener(subject) { 1.201 + let screen = subject.wrappedJSObject; 1.202 + let { mozOrientation, screenOrientation } = screen; 1.203 + 1.204 + let newWidth = width; 1.205 + let newHeight = height; 1.206 + // If we have an orientation different than the startup one, 1.207 + // we switch the sizes 1.208 + if (screenOrientation != defaultOrientation) { 1.209 + newWidth = height; 1.210 + newHeight = width; 1.211 + } 1.212 + 1.213 + // If the current app doesn't supports the current screen orientation 1.214 + // still resize the window, but rotate its frame so that 1.215 + // it is displayed rotated on the side 1.216 + let shouldFlip = mozOrientation != screenOrientation; 1.217 + 1.218 + resize(newWidth, newHeight, dpi, shouldFlip); 1.219 + }, 'simulator-adjust-window-size', false); 1.220 + 1.221 + // A utility function like console.log() for printing to the terminal window 1.222 + // Uses dump(), but enables it first, if necessary 1.223 + function print() { 1.224 + let dump_enabled = 1.225 + Services.prefs.getBoolPref('browser.dom.window.dump.enabled'); 1.226 + 1.227 + if (!dump_enabled) 1.228 + Services.prefs.setBoolPref('browser.dom.window.dump.enabled', true); 1.229 + 1.230 + dump(Array.prototype.join.call(arguments, ' ') + '\n'); 1.231 + 1.232 + if (!dump_enabled) 1.233 + Services.prefs.setBoolPref('browser.dom.window.dump.enabled', false); 1.234 + } 1.235 + 1.236 + // Print usage info for --screen and exit 1.237 + function usage() { 1.238 + // Documentation for the --screen argument 1.239 + let msg = 1.240 + 'The --screen argument specifies the desired resolution and\n' + 1.241 + 'pixel density of the simulated device screen. Use it like this:\n' + 1.242 + '\t--screen=WIDTHxHEIGHT\t\t\t// E.g.: --screen=320x480\n' + 1.243 + '\t--screen=WIDTHxHEIGHT@DOTS_PER_INCH\t// E.g.: --screen=480x800@250\n' + 1.244 + '\t--screen=full\t\t\t\t// run in fullscreen mode\n' + 1.245 + '\nYou can also specify certain device names:\n'; 1.246 + for(let p in screens) 1.247 + msg += '\t--screen=' + p + '\t// ' + screens[p].name + '\n'; 1.248 + msg += 1.249 + '\nAdd a ! to the end of a screen specification to rescale the\n' + 1.250 + 'screen so that it is shown at actual size on your monitor:\n' + 1.251 + '\t--screen=nexus_s!\n' + 1.252 + '\t--screen=320x480@200!\n' 1.253 + ; 1.254 + 1.255 + // Display the usage message 1.256 + print(msg); 1.257 + 1.258 + // Exit the b2g client 1.259 + Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit); 1.260 + } 1.261 +});