| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview | 6 * @fileoverview |
| 7 * Class handling user-facing aspects of the client session. | 7 * Class handling user-facing aspects of the client session. |
| 8 */ | 8 */ |
| 9 | 9 |
| 10 'use strict'; | 10 'use strict'; |
| 11 | 11 |
| 12 /** @suppress {duplicate} */ | 12 /** @suppress {duplicate} */ |
| 13 var remoting = remoting || {}; | 13 var remoting = remoting || {}; |
| 14 | 14 |
| 15 /** | 15 /** |
| 16 * True to enable mouse lock. | 16 * True to enable mouse lock. |
| 17 * This is currently disabled because the current client plugin does not | 17 * This is currently disabled because the current client plugin does not |
| 18 * properly handle mouse lock and delegated large cursors at the same time. | 18 * properly handle mouse lock and delegated large cursors at the same time. |
| 19 * This should be re-enabled (by removing this flag) once a version of | 19 * This should be re-enabled (by removing this flag) once a version of |
| 20 * the plugin that supports both has reached Chrome Stable channel. | 20 * the plugin that supports both has reached Chrome Stable channel. |
| 21 * (crbug.com/429322). | 21 * (crbug.com/429322). |
| 22 * | 22 * |
| 23 * @type {boolean} | 23 * @type {boolean} |
| 24 */ | 24 */ |
| 25 remoting.enableMouseLock = false; | 25 remoting.enableMouseLock = false; |
| 26 | 26 |
| 27 /** | 27 /** |
| 28 * @param {remoting.ClientSession} session | 28 * @param {remoting.ClientSession} session |
| 29 * @param {HTMLElement} container | 29 * @param {HTMLElement} container |
| 30 * @param {string} hostDisplayName A human-readable name for the host. | 30 * @param {remoting.Host} host |
| 31 * @param {string} hostId The host identifier for Me2Me, or empty for IT2Me. | |
| 32 * Mixed into authentication hashes for some authentication methods. | |
| 33 * @param {remoting.DesktopConnectedView.Mode} mode The mode of this connection. | 31 * @param {remoting.DesktopConnectedView.Mode} mode The mode of this connection. |
| 34 * @param {string} defaultRemapKeys The default set of remap keys, to use | 32 * @param {string} defaultRemapKeys The default set of remap keys, to use |
| 35 * when the client doesn't define any. | 33 * when the client doesn't define any. |
| 36 * @param {function(remoting.Error, remoting.ClientPlugin): void} onInitialized | 34 * @param {function(remoting.Error, remoting.ClientPlugin): void} onInitialized |
| 37 * @constructor | 35 * @constructor |
| 38 * @extends {base.EventSourceImpl} | 36 * @extends {base.EventSourceImpl} |
| 39 */ | 37 */ |
| 40 remoting.DesktopConnectedView = function(session, container, hostDisplayName, | 38 remoting.DesktopConnectedView = function(session, container, host, mode, |
| 41 hostId, mode, defaultRemapKeys, | 39 defaultRemapKeys, onInitialized) { |
| 42 onInitialized) { | |
| 43 this.session_ = session; | 40 this.session_ = session; |
| 44 | 41 |
| 45 /** @type {HTMLElement} @private */ | 42 /** @type {HTMLElement} @private */ |
| 46 this.container_ = container; | 43 this.container_ = container; |
| 47 | 44 |
| 48 /** @type {remoting.ClientPlugin} @private */ | 45 /** @type {remoting.ClientPlugin} @private */ |
| 49 this.plugin_ = null; | 46 this.plugin_ = null; |
| 50 | 47 |
| 51 /** @private */ | 48 /** @private */ |
| 52 this.hostDisplayName_ = hostDisplayName; | 49 this.host_ = host; |
| 53 | |
| 54 /** @private */ | |
| 55 this.hostId_ = hostId; | |
| 56 | 50 |
| 57 /** @private */ | 51 /** @private */ |
| 58 this.mode_ = mode; | 52 this.mode_ = mode; |
| 59 | 53 |
| 60 /** @type {boolean} @private */ | |
| 61 this.shrinkToFit_ = true; | |
| 62 | |
| 63 /** @type {boolean} @private */ | |
| 64 this.resizeToClient_ = true; | |
| 65 | |
| 66 /** @type {number} @private */ | |
| 67 this.desktopScale_ = 1.0; | |
| 68 | |
| 69 /** @type {string} @private */ | 54 /** @type {string} @private */ |
| 70 this.defaultRemapKeys_ = defaultRemapKeys; | 55 this.defaultRemapKeys_ = defaultRemapKeys; |
| 71 | 56 |
| 72 /** @type {string} @private */ | 57 /** @type {string} @private */ |
| 73 this.remapKeys_ = ''; | 58 this.remapKeys_ = ''; |
| 74 | 59 |
| 75 /** | 60 /** |
| 76 * Called when the UI is finished initializing. | 61 * Called when the UI is finished initializing. |
| 77 * @type {function(remoting.Error, remoting.ClientPlugin):void} | 62 * @type {function(remoting.Error, remoting.ClientPlugin):void} |
| 78 */ | 63 */ |
| 79 this.onInitialized_ = onInitialized; | 64 this.onInitialized_ = onInitialized; |
| 80 | 65 |
| 81 /** @type {function(boolean=):void} @private */ | 66 /** @type {function(boolean=):void} @private */ |
| 82 this.callOnFullScreenChanged_ = this.onFullScreenChanged_.bind(this) | 67 this.callOnFullScreenChanged_ = this.onFullScreenChanged_.bind(this) |
| 83 | 68 |
| 84 /** @private */ | 69 /** @private */ |
| 85 this.callPluginLostFocus_ = this.pluginLostFocus_.bind(this); | 70 this.callPluginLostFocus_ = this.pluginLostFocus_.bind(this); |
| 86 /** @private */ | 71 /** @private */ |
| 87 this.callPluginGotFocus_ = this.pluginGotFocus_.bind(this); | 72 this.callPluginGotFocus_ = this.pluginGotFocus_.bind(this); |
| 88 | 73 |
| 89 /** @type {number?} @private */ | |
| 90 this.notifyClientResolutionTimer_ = null; | |
| 91 | |
| 92 /** @type {Element} @private */ | 74 /** @type {Element} @private */ |
| 93 this.mouseCursorOverlay_ = | 75 this.mouseCursorOverlay_ = |
| 94 this.container_.querySelector('.mouse-cursor-overlay'); | 76 this.container_.querySelector('.mouse-cursor-overlay'); |
| 95 | 77 |
| 78 /** @private {remoting.DesktopViewport} */ |
| 79 this.viewport_ = null; |
| 80 |
| 96 /** @type {Element} */ | 81 /** @type {Element} */ |
| 97 var img = this.mouseCursorOverlay_; | 82 var img = this.mouseCursorOverlay_; |
| 98 /** @param {Event} event @private */ | 83 /** @param {Event} event @private */ |
| 99 this.updateMouseCursorPosition_ = function(event) { | 84 this.updateMouseCursorPosition_ = function(event) { |
| 100 img.style.top = event.y + 'px'; | 85 img.style.top = event.y + 'px'; |
| 101 img.style.left = event.x + 'px'; | 86 img.style.left = event.x + 'px'; |
| 102 }; | 87 }; |
| 103 | 88 |
| 104 /** @type {number?} @private */ | |
| 105 this.bumpScrollTimer_ = null; | |
| 106 | |
| 107 // Bump-scroll test variables. Override to use a fake value for the width | |
| 108 // and height of the client plugin so that bump-scrolling can be tested | |
| 109 // without relying on the actual size of the host desktop. | |
| 110 /** @type {number} @private */ | |
| 111 this.pluginWidthForBumpScrollTesting = 0; | |
| 112 /** @type {number} @private */ | |
| 113 this.pluginHeightForBumpScrollTesting = 0; | |
| 114 | |
| 115 /** @type {remoting.VideoFrameRecorder} @private */ | 89 /** @type {remoting.VideoFrameRecorder} @private */ |
| 116 this.videoFrameRecorder_ = null; | 90 this.videoFrameRecorder_ = null; |
| 117 }; | 91 }; |
| 118 | 92 |
| 119 base.extend(remoting.DesktopConnectedView, base.EventSourceImpl); | 93 base.extend(remoting.DesktopConnectedView, base.EventSourceImpl); |
| 120 | 94 |
| 121 /** @enum {string} */ | |
| 122 remoting.DesktopConnectedView.Events = { | |
| 123 bumpScrollStarted: 'bumpScrollStarted', | |
| 124 bumpScrollStopped: 'bumpScrollStopped' | |
| 125 }; | |
| 126 | |
| 127 // The mode of this session. | 95 // The mode of this session. |
| 128 /** @enum {number} */ | 96 /** @enum {number} */ |
| 129 remoting.DesktopConnectedView.Mode = { | 97 remoting.DesktopConnectedView.Mode = { |
| 130 IT2ME: 0, | 98 IT2ME: 0, |
| 131 ME2ME: 1, | 99 ME2ME: 1, |
| 132 APP_REMOTING: 2 | 100 APP_REMOTING: 2 |
| 133 }; | 101 }; |
| 134 | 102 |
| 135 // Keys for per-host settings. | 103 // Keys for per-host settings. |
| 136 remoting.DesktopConnectedView.KEY_REMAP_KEYS = 'remapKeys'; | 104 remoting.DesktopConnectedView.KEY_REMAP_KEYS = 'remapKeys'; |
| 137 remoting.DesktopConnectedView.KEY_RESIZE_TO_CLIENT = 'resizeToClient'; | 105 remoting.DesktopConnectedView.KEY_RESIZE_TO_CLIENT = 'resizeToClient'; |
| 138 remoting.DesktopConnectedView.KEY_SHRINK_TO_FIT = 'shrinkToFit'; | 106 remoting.DesktopConnectedView.KEY_SHRINK_TO_FIT = 'shrinkToFit'; |
| 139 remoting.DesktopConnectedView.KEY_DESKTOP_SCALE = 'desktopScale'; | 107 remoting.DesktopConnectedView.KEY_DESKTOP_SCALE = 'desktopScale'; |
| 140 | 108 |
| 141 /** | 109 /** |
| 142 * Get host display name. | 110 * Get host display name. |
| 143 * | 111 * |
| 144 * @return {string} | 112 * @return {string} |
| 145 */ | 113 */ |
| 146 remoting.DesktopConnectedView.prototype.getHostDisplayName = function() { | 114 remoting.DesktopConnectedView.prototype.getHostDisplayName = function() { |
| 147 return this.hostDisplayName_; | 115 return this.host_.hostName; |
| 148 }; | 116 }; |
| 149 | 117 |
| 150 /** | 118 /** |
| 151 * @return {remoting.DesktopConnectedView.Mode} The current state. | 119 * @return {remoting.DesktopConnectedView.Mode} The current state. |
| 152 */ | 120 */ |
| 153 remoting.DesktopConnectedView.prototype.getMode = function() { | 121 remoting.DesktopConnectedView.prototype.getMode = function() { |
| 154 return this.mode_; | 122 return this.mode_; |
| 155 }; | 123 }; |
| 156 | 124 |
| 157 /** | 125 /** |
| 158 * @return {boolean} True if shrink-to-fit is enabled; false otherwise. | 126 * @return {boolean} True if shrink-to-fit is enabled; false otherwise. |
| 159 */ | 127 */ |
| 160 remoting.DesktopConnectedView.prototype.getShrinkToFit = function() { | 128 remoting.DesktopConnectedView.prototype.getShrinkToFit = function() { |
| 161 return this.shrinkToFit_; | 129 if (this.viewport_) { |
| 130 return this.viewport_.getShrinkToFit(); |
| 131 } |
| 162 }; | 132 }; |
| 163 | 133 |
| 164 /** | 134 /** |
| 165 * @return {boolean} True if resize-to-client is enabled; false otherwise. | 135 * @return {boolean} True if resize-to-client is enabled; false otherwise. |
| 166 */ | 136 */ |
| 167 remoting.DesktopConnectedView.prototype.getResizeToClient = function() { | 137 remoting.DesktopConnectedView.prototype.getResizeToClient = function() { |
| 168 return this.resizeToClient_; | 138 if (this.viewport_) { |
| 139 return this.viewport_.getResizeToClient(); |
| 140 } |
| 169 }; | 141 }; |
| 170 | 142 |
| 171 /** | 143 /** |
| 172 * @return {Element} The element that should host the plugin. | 144 * @return {Element} The element that should host the plugin. |
| 145 * @private |
| 173 */ | 146 */ |
| 174 remoting.DesktopConnectedView.prototype.getPluginContainer = function() { | 147 remoting.DesktopConnectedView.prototype.getPluginContainer_ = function() { |
| 175 return this.container_.querySelector('.client-plugin-container') | 148 return this.container_.querySelector('.client-plugin-container'); |
| 176 }; | 149 }; |
| 177 | 150 |
| 178 /** | 151 /** |
| 179 * @return {{width: number, height: number}} The height of the window's client | |
| 180 * area. This differs between apps v1 and apps v2 due to the custom window | |
| 181 * borders used by the latter. | |
| 182 * TODO: make private | |
| 183 */ | |
| 184 remoting.DesktopConnectedView.prototype.getClientArea_ = function() { | |
| 185 return remoting.windowFrame ? | |
| 186 remoting.windowFrame.getClientArea() : | |
| 187 { 'width': window.innerWidth, 'height': window.innerHeight }; | |
| 188 }; | |
| 189 | |
| 190 /** | |
| 191 * Notifies the host of the client's current dimensions and DPI. | 152 * Notifies the host of the client's current dimensions and DPI. |
| 192 * Also takes into account per-host scaling factor, if configured. | 153 * Also takes into account per-host scaling factor, if configured. |
| 193 * TODO: private | 154 * TODO: private |
| 194 */ | 155 */ |
| 195 remoting.DesktopConnectedView.prototype.notifyClientResolution_ = function() { | 156 remoting.DesktopConnectedView.prototype.notifyClientResolution_ = function() { |
| 196 var clientArea = this.getClientArea_(); | 157 if (this.viewport_) { |
| 197 this.plugin_.notifyClientResolution(clientArea.width * this.desktopScale_, | 158 this.viewport_.resizeHostDesktop(); |
| 198 clientArea.height * this.desktopScale_, | 159 } |
| 199 window.devicePixelRatio); | 160 }; |
| 161 |
| 162 /** @return {remoting.DesktopViewport} */ |
| 163 remoting.DesktopConnectedView.prototype.getViewportForTesting = function() { |
| 164 return this.viewport_; |
| 200 }; | 165 }; |
| 201 | 166 |
| 202 /** | 167 /** |
| 203 * Adds <embed> element to the UI container and readies the session object. | 168 * Adds <embed> element to the UI container and readies the session object. |
| 204 * | 169 * |
| 205 * @param {function(string, string):boolean} onExtensionMessage The handler for | 170 * @param {function(string, string):boolean} onExtensionMessage The handler for |
| 206 * protocol extension messages. Returns true if a message is recognized; | 171 * protocol extension messages. Returns true if a message is recognized; |
| 207 * false otherwise. | 172 * false otherwise. |
| 208 * @param {Array<string>} requiredCapabilities A list of capabilities | 173 * @param {Array.<string>} requiredCapabilities A list of capabilities |
| 209 * required by this application. | 174 * required by this application. |
| 210 */ | 175 */ |
| 211 remoting.DesktopConnectedView.prototype.createPluginAndConnect = | 176 remoting.DesktopConnectedView.prototype.createPluginAndConnect = |
| 212 function(onExtensionMessage, requiredCapabilities) { | 177 function(onExtensionMessage, requiredCapabilities) { |
| 213 this.plugin_ = remoting.ClientPlugin.factory.createPlugin( | 178 this.plugin_ = remoting.ClientPlugin.factory.createPlugin( |
| 214 this.getPluginContainer(), | 179 this.getPluginContainer_(), |
| 215 onExtensionMessage, requiredCapabilities); | 180 onExtensionMessage, requiredCapabilities); |
| 216 remoting.HostSettings.load(this.hostId_, | 181 var that = this; |
| 217 this.onHostSettingsLoaded_.bind(this)); | 182 this.host_.options().load().then(function(){ |
| 183 that.plugin_.initialize(that.onPluginInitialized_.bind(that)); |
| 184 }); |
| 218 }; | 185 }; |
| 219 | 186 |
| 220 /** | 187 /** |
| 221 * @param {Object<string|boolean|number>} options The current options for the | |
| 222 * host, or {} if this client has no saved settings for the host. | |
| 223 * @private | |
| 224 */ | |
| 225 remoting.DesktopConnectedView.prototype.onHostSettingsLoaded_ = function( | |
| 226 options) { | |
| 227 if (remoting.DesktopConnectedView.KEY_REMAP_KEYS in options && | |
| 228 typeof(options[remoting.DesktopConnectedView.KEY_REMAP_KEYS]) == | |
| 229 'string') { | |
| 230 this.remapKeys_ = /** @type {string} */ | |
| 231 (options[remoting.DesktopConnectedView.KEY_REMAP_KEYS]); | |
| 232 } | |
| 233 if (remoting.DesktopConnectedView.KEY_RESIZE_TO_CLIENT in options && | |
| 234 typeof(options[remoting.DesktopConnectedView.KEY_RESIZE_TO_CLIENT]) == | |
| 235 'boolean') { | |
| 236 this.resizeToClient_ = /** @type {boolean} */ | |
| 237 (options[remoting.DesktopConnectedView.KEY_RESIZE_TO_CLIENT]); | |
| 238 } | |
| 239 if (remoting.DesktopConnectedView.KEY_SHRINK_TO_FIT in options && | |
| 240 typeof(options[remoting.DesktopConnectedView.KEY_SHRINK_TO_FIT]) == | |
| 241 'boolean') { | |
| 242 this.shrinkToFit_ = /** @type {boolean} */ | |
| 243 (options[remoting.DesktopConnectedView.KEY_SHRINK_TO_FIT]); | |
| 244 } | |
| 245 if (remoting.DesktopConnectedView.KEY_DESKTOP_SCALE in options && | |
| 246 typeof(options[remoting.DesktopConnectedView.KEY_DESKTOP_SCALE]) == | |
| 247 'number') { | |
| 248 this.desktopScale_ = /** @type {number} */ | |
| 249 (options[remoting.DesktopConnectedView.KEY_DESKTOP_SCALE]); | |
| 250 } | |
| 251 | |
| 252 /** @param {boolean} result */ | |
| 253 this.plugin_.initialize(this.onPluginInitialized_.bind(this)); | |
| 254 }; | |
| 255 | |
| 256 /** | |
| 257 * @param {boolean} initialized | 188 * @param {boolean} initialized |
| 258 */ | 189 */ |
| 259 remoting.DesktopConnectedView.prototype.onPluginInitialized_ = function( | 190 remoting.DesktopConnectedView.prototype.onPluginInitialized_ = function( |
| 260 initialized) { | 191 initialized) { |
| 261 if (!initialized) { | 192 if (!initialized) { |
| 262 console.error('ERROR: remoting plugin not loaded'); | 193 console.error('ERROR: remoting plugin not loaded'); |
| 263 this.onInitialized_(remoting.Error.MISSING_PLUGIN, this.plugin_); | 194 this.onInitialized_(remoting.Error.MISSING_PLUGIN, this.plugin_); |
| 264 return; | 195 return; |
| 265 } | 196 } |
| 266 | 197 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 286 this.applyRemapKeys_(true); | 217 this.applyRemapKeys_(true); |
| 287 } | 218 } |
| 288 | 219 |
| 289 // TODO(wez): Only allow mouse lock if the app has the pointerLock permission. | 220 // TODO(wez): Only allow mouse lock if the app has the pointerLock permission. |
| 290 // Enable automatic mouse-lock. | 221 // Enable automatic mouse-lock. |
| 291 if (remoting.enableMouseLock && | 222 if (remoting.enableMouseLock && |
| 292 this.plugin_.hasFeature(remoting.ClientPlugin.Feature.ALLOW_MOUSE_LOCK)) { | 223 this.plugin_.hasFeature(remoting.ClientPlugin.Feature.ALLOW_MOUSE_LOCK)) { |
| 293 this.plugin_.allowMouseLock(); | 224 this.plugin_.allowMouseLock(); |
| 294 } | 225 } |
| 295 | 226 |
| 296 this.plugin_.setDesktopShapeUpdateHandler( | |
| 297 this.onDesktopShapeChanged_.bind(this)); | |
| 298 this.plugin_.setDesktopSizeUpdateHandler( | |
| 299 this.onDesktopSizeChanged_.bind(this)); | |
| 300 this.plugin_.setMouseCursorHandler(this.updateMouseCursorImage_.bind(this)); | 227 this.plugin_.setMouseCursorHandler(this.updateMouseCursorImage_.bind(this)); |
| 301 | 228 |
| 302 this.onInitialized_(remoting.Error.NONE, this.plugin_); | 229 this.onInitialized_(remoting.Error.NONE, this.plugin_); |
| 303 }; | 230 }; |
| 304 | 231 |
| 305 /** | 232 /** |
| 306 * This is a callback that gets called when the plugin notifies us of a change | |
| 307 * in the size of the remote desktop. | |
| 308 * | |
| 309 * @return {void} Nothing. | |
| 310 * @private | |
| 311 */ | |
| 312 remoting.DesktopConnectedView.prototype.onDesktopSizeChanged_ = function() { | |
| 313 console.log('desktop size changed: ' + | |
| 314 this.plugin_.getDesktopWidth() + 'x' + | |
| 315 this.plugin_.getDesktopHeight() +' @ ' + | |
| 316 this.plugin_.getDesktopXDpi() + 'x' + | |
| 317 this.plugin_.getDesktopYDpi() + ' DPI'); | |
| 318 this.updateDimensions(); | |
| 319 this.updateScrollbarVisibility(); | |
| 320 }; | |
| 321 | |
| 322 /** | |
| 323 * Sets the non-click-through area of the client in response to notifications | |
| 324 * from the plugin of desktop shape changes. | |
| 325 * | |
| 326 * @param {Array<Array<number>>} rects List of rectangles comprising the | |
| 327 * desktop shape. | |
| 328 * @return {void} Nothing. | |
| 329 * @private | |
| 330 */ | |
| 331 remoting.DesktopConnectedView.prototype.onDesktopShapeChanged_ = function( | |
| 332 rects) { | |
| 333 // Build the list of rects for the input region. | |
| 334 var inputRegion = []; | |
| 335 for (var i = 0; i < rects.length; ++i) { | |
| 336 var rect = {}; | |
| 337 rect.left = rects[i][0]; | |
| 338 rect.top = rects[i][1]; | |
| 339 rect.width = rects[i][2]; | |
| 340 rect.height = rects[i][3]; | |
| 341 inputRegion.push(rect); | |
| 342 } | |
| 343 | |
| 344 remoting.windowShape.setDesktopRects(inputRegion); | |
| 345 }; | |
| 346 | |
| 347 /** | |
| 348 * This is a callback that gets called when the window is resized. | 233 * This is a callback that gets called when the window is resized. |
| 349 * | 234 * |
| 350 * @return {void} Nothing. | 235 * @return {void} Nothing. |
| 351 */ | 236 */ |
| 352 remoting.DesktopConnectedView.prototype.onResize = function() { | 237 remoting.DesktopConnectedView.prototype.onResize = function() { |
| 353 this.updateDimensions(); | 238 if (this.viewport_) { |
| 354 | 239 this.viewport_.onResize(); |
| 355 if (this.notifyClientResolutionTimer_) { | |
| 356 window.clearTimeout(this.notifyClientResolutionTimer_); | |
| 357 this.notifyClientResolutionTimer_ = null; | |
| 358 } | 240 } |
| 359 | |
| 360 // Defer notifying the host of the change until the window stops resizing, to | |
| 361 // avoid overloading the control channel with notifications. | |
| 362 if (this.resizeToClient_) { | |
| 363 var kResizeRateLimitMs = 1000; | |
| 364 if (this.session_.hasCapability( | |
| 365 remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS)) { | |
| 366 kResizeRateLimitMs = 250; | |
| 367 } | |
| 368 var clientArea = this.getClientArea_(); | |
| 369 this.notifyClientResolutionTimer_ = window.setTimeout( | |
| 370 this.notifyClientResolution_.bind(this), | |
| 371 kResizeRateLimitMs); | |
| 372 } | |
| 373 | |
| 374 // If bump-scrolling is enabled, adjust the plugin margins to fully utilize | |
| 375 // the new window area. | |
| 376 this.resetScroll_(); | |
| 377 | |
| 378 this.updateScrollbarVisibility(); | |
| 379 }; | 241 }; |
| 380 | 242 |
| 381 /** | 243 /** |
| 382 * Callback that the plugin invokes to indicate when the connection is | 244 * Callback that the plugin invokes to indicate when the connection is |
| 383 * ready. | 245 * ready. |
| 384 * | 246 * |
| 385 * @param {boolean} ready True if the connection is ready. | 247 * @param {boolean} ready True if the connection is ready. |
| 386 */ | 248 */ |
| 387 remoting.DesktopConnectedView.prototype.onConnectionReady = function(ready) { | 249 remoting.DesktopConnectedView.prototype.onConnectionReady = function(ready) { |
| 388 if (!ready) { | 250 if (!ready) { |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 428 if (remoting.optionsMenu) { | 290 if (remoting.optionsMenu) { |
| 429 remoting.optionsMenu.setDesktopConnectedView(null); | 291 remoting.optionsMenu.setDesktopConnectedView(null); |
| 430 } | 292 } |
| 431 | 293 |
| 432 document.body.classList.remove('connected'); | 294 document.body.classList.remove('connected'); |
| 433 this.container_.removeEventListener( | 295 this.container_.removeEventListener( |
| 434 'mousemove', this.updateMouseCursorPosition_, true); | 296 'mousemove', this.updateMouseCursorPosition_, true); |
| 435 // Stop listening for full-screen events. | 297 // Stop listening for full-screen events. |
| 436 remoting.fullscreen.removeListener(this.callOnFullScreenChanged_); | 298 remoting.fullscreen.removeListener(this.callOnFullScreenChanged_); |
| 437 | 299 |
| 300 base.dispose(this.viewport_); |
| 301 this.viewport_ = null; |
| 438 } else { | 302 } else { |
| 303 this.viewport_ = new remoting.DesktopViewport( |
| 304 document.getElementById('scroller'), |
| 305 this.plugin_.hostDesktop(), |
| 306 this.host_.options()); |
| 439 if (remoting.windowFrame) { | 307 if (remoting.windowFrame) { |
| 440 remoting.windowFrame.setDesktopConnectedView(this); | 308 remoting.windowFrame.setDesktopConnectedView(this); |
| 441 } | 309 } |
| 442 if (remoting.toolbar) { | 310 if (remoting.toolbar) { |
| 443 remoting.toolbar.setDesktopConnectedView(this); | 311 remoting.toolbar.setDesktopConnectedView(this); |
| 444 } | 312 } |
| 445 if (remoting.optionsMenu) { | 313 if (remoting.optionsMenu) { |
| 446 remoting.optionsMenu.setDesktopConnectedView(this); | 314 remoting.optionsMenu.setDesktopConnectedView(this); |
| 447 } | 315 } |
| 448 | 316 |
| 449 if (this.resizeToClient_) { | |
| 450 this.notifyClientResolution_(); | |
| 451 } | |
| 452 | |
| 453 document.body.classList.add('connected'); | 317 document.body.classList.add('connected'); |
| 454 this.container_.addEventListener( | 318 this.container_.addEventListener( |
| 455 'mousemove', this.updateMouseCursorPosition_, true); | 319 'mousemove', this.updateMouseCursorPosition_, true); |
| 456 // Activate full-screen related UX. | 320 // Activate full-screen related UX. |
| 457 remoting.fullscreen.addListener(this.callOnFullScreenChanged_); | 321 remoting.fullscreen.addListener(this.callOnFullScreenChanged_); |
| 458 this.onDesktopSizeChanged_(); | |
| 459 this.setFocusHandlers_(); | 322 this.setFocusHandlers_(); |
| 460 } | 323 } |
| 461 }; | 324 }; |
| 462 | 325 |
| 463 /** | 326 /** |
| 464 * @return {{top: number, left:number}} The top-left corner of the plugin. | |
| 465 */ | |
| 466 remoting.DesktopConnectedView.prototype.getPluginPositionForTesting = function( | |
| 467 ) { | |
| 468 var style = this.container_.style; | |
| 469 return { | |
| 470 top: parseFloat(style.marginTop), | |
| 471 left: parseFloat(style.marginLeft) | |
| 472 }; | |
| 473 }; | |
| 474 | |
| 475 /** | |
| 476 * Constrains the focus to the plugin element. | 327 * Constrains the focus to the plugin element. |
| 477 * @private | 328 * @private |
| 478 */ | 329 */ |
| 479 remoting.DesktopConnectedView.prototype.setFocusHandlers_ = function() { | 330 remoting.DesktopConnectedView.prototype.setFocusHandlers_ = function() { |
| 480 this.plugin_.element().addEventListener( | 331 this.plugin_.element().addEventListener( |
| 481 'focus', this.callPluginGotFocus_, false); | 332 'focus', this.callPluginGotFocus_, false); |
| 482 this.plugin_.element().addEventListener( | 333 this.plugin_.element().addEventListener( |
| 483 'blur', this.callPluginLostFocus_, false); | 334 'blur', this.callPluginLostFocus_, false); |
| 484 this.plugin_.element().focus(); | 335 this.plugin_.element().focus(); |
| 485 }; | 336 }; |
| 486 | 337 |
| 487 /** | 338 /** |
| 488 * Set the shrink-to-fit and resize-to-client flags and save them if this is | 339 * Set the shrink-to-fit and resize-to-client flags and save them if this is |
| 489 * a Me2Me connection. | 340 * a Me2Me connection. |
| 490 * | 341 * |
| 491 * @param {boolean} shrinkToFit True if the remote desktop should be scaled | 342 * @param {boolean} shrinkToFit True if the remote desktop should be scaled |
| 492 * down if it is larger than the client window; false if scroll-bars | 343 * down if it is larger than the client window; false if scroll-bars |
| 493 * should be added in this case. | 344 * should be added in this case. |
| 494 * @param {boolean} resizeToClient True if window resizes should cause the | 345 * @param {boolean} resizeToClient True if window resizes should cause the |
| 495 * host to attempt to resize its desktop to match the client window size; | 346 * host to attempt to resize its desktop to match the client window size; |
| 496 * false to disable this behaviour for subsequent window resizes--the | 347 * false to disable this behaviour for subsequent window resizes--the |
| 497 * current host desktop size is not restored in this case. | 348 * current host desktop size is not restored in this case. |
| 498 * @return {void} Nothing. | 349 * @return {void} Nothing. |
| 499 */ | 350 */ |
| 500 remoting.DesktopConnectedView.prototype.setScreenMode = | 351 remoting.DesktopConnectedView.prototype.setScreenMode = |
| 501 function(shrinkToFit, resizeToClient) { | 352 function(shrinkToFit, resizeToClient) { |
| 502 if (resizeToClient && !this.resizeToClient_) { | 353 this.viewport_.setScreenMode(shrinkToFit, resizeToClient); |
| 503 this.notifyClientResolution_(); | |
| 504 } | |
| 505 | |
| 506 // If enabling shrink, reset bump-scroll offsets. | |
| 507 var needsScrollReset = shrinkToFit && !this.shrinkToFit_; | |
| 508 | |
| 509 this.shrinkToFit_ = shrinkToFit; | |
| 510 this.resizeToClient_ = resizeToClient; | |
| 511 this.updateScrollbarVisibility(); | |
| 512 | |
| 513 if (this.hostId_ != '') { | |
| 514 var options = {}; | |
| 515 options[remoting.DesktopConnectedView.KEY_SHRINK_TO_FIT] = | |
| 516 this.shrinkToFit_; | |
| 517 options[remoting.DesktopConnectedView.KEY_RESIZE_TO_CLIENT] = | |
| 518 this.resizeToClient_; | |
| 519 remoting.HostSettings.save(this.hostId_, options); | |
| 520 } | |
| 521 | |
| 522 this.updateDimensions(); | |
| 523 if (needsScrollReset) { | |
| 524 this.resetScroll_(); | |
| 525 } | |
| 526 }; | 354 }; |
| 527 | 355 |
| 528 /** | 356 /** |
| 529 * Sets and stores the scale factor to apply to host sizing requests. | |
| 530 * The desktopScale applies to the dimensions reported to the host, not | |
| 531 * to the client DPI reported to it. | |
| 532 * | |
| 533 * @param {number} desktopScale Scale factor to apply. | |
| 534 */ | |
| 535 remoting.DesktopConnectedView.prototype.setDesktopScale = function( | |
| 536 desktopScale) { | |
| 537 this.desktopScale_ = desktopScale; | |
| 538 | |
| 539 // onResize() will update the plugin size and scrollbars for the new | |
| 540 // scaled plugin dimensions, and send a client resolution notification. | |
| 541 this.onResize(); | |
| 542 | |
| 543 // Save the new desktop scale setting. | |
| 544 var options = {}; | |
| 545 options[remoting.DesktopConnectedView.KEY_DESKTOP_SCALE] = this.desktopScale_; | |
| 546 remoting.HostSettings.save(this.hostId_, options); | |
| 547 }; | |
| 548 | |
| 549 /** | |
| 550 * Called when the full-screen status has changed, either via the | 357 * Called when the full-screen status has changed, either via the |
| 551 * remoting.Fullscreen class, or via a system event such as the Escape key | 358 * remoting.Fullscreen class, or via a system event such as the Escape key |
| 552 * | 359 * |
| 553 * @param {boolean=} fullscreen True if the app is entering full-screen mode; | 360 * @param {boolean=} fullscreen True if the app is entering full-screen mode; |
| 554 * false if it is leaving it. | 361 * false if it is leaving it. |
| 555 * @private | 362 * @private |
| 556 */ | 363 */ |
| 557 remoting.DesktopConnectedView.prototype.onFullScreenChanged_ = function ( | 364 remoting.DesktopConnectedView.prototype.onFullScreenChanged_ = function ( |
| 558 fullscreen) { | 365 fullscreen) { |
| 559 this.enableBumpScroll_(fullscreen); | 366 if (this.viewport_) { |
| 367 this.viewport_.enableBumpScroll(fullscreen); |
| 368 } |
| 560 }; | 369 }; |
| 561 | 370 |
| 562 /** | 371 /** |
| 563 * Callback function called when the plugin element gets focus. | 372 * Callback function called when the plugin element gets focus. |
| 564 */ | 373 */ |
| 565 remoting.DesktopConnectedView.prototype.pluginGotFocus_ = function() { | 374 remoting.DesktopConnectedView.prototype.pluginGotFocus_ = function() { |
| 566 remoting.clipboard.initiateToHost(); | 375 remoting.clipboard.initiateToHost(); |
| 567 }; | 376 }; |
| 568 | 377 |
| 569 /** | 378 /** |
| (...skipping 22 matching lines...) Expand all Loading... |
| 592 function(url, hotspotX, hotspotY) { | 401 function(url, hotspotX, hotspotY) { |
| 593 this.mouseCursorOverlay_.hidden = !url; | 402 this.mouseCursorOverlay_.hidden = !url; |
| 594 if (url) { | 403 if (url) { |
| 595 this.mouseCursorOverlay_.style.marginLeft = '-' + hotspotX + 'px'; | 404 this.mouseCursorOverlay_.style.marginLeft = '-' + hotspotX + 'px'; |
| 596 this.mouseCursorOverlay_.style.marginTop = '-' + hotspotY + 'px'; | 405 this.mouseCursorOverlay_.style.marginTop = '-' + hotspotY + 'px'; |
| 597 this.mouseCursorOverlay_.src = url; | 406 this.mouseCursorOverlay_.src = url; |
| 598 } | 407 } |
| 599 }; | 408 }; |
| 600 | 409 |
| 601 /** | 410 /** |
| 602 * Enable or disable bump-scrolling. When disabling bump scrolling, also reset | |
| 603 * the scroll offsets to (0, 0). | |
| 604 * @param {boolean=} enable True to enable bump-scrolling, false to disable it. | |
| 605 * @private | |
| 606 */ | |
| 607 remoting.DesktopConnectedView.prototype.enableBumpScroll_ = function(enable) { | |
| 608 var element = /** @type{HTMLElement} */ (document.documentElement); | |
| 609 if (enable) { | |
| 610 /** @type {null|function(Event):void} */ | |
| 611 this.onMouseMoveRef_ = this.onMouseMove_.bind(this); | |
| 612 element.addEventListener('mousemove', this.onMouseMoveRef_, false); | |
| 613 } else { | |
| 614 element.removeEventListener('mousemove', this.onMouseMoveRef_, false); | |
| 615 this.onMouseMoveRef_ = null; | |
| 616 this.resetScroll_(); | |
| 617 } | |
| 618 }; | |
| 619 | |
| 620 remoting.DesktopConnectedView.prototype.resetScroll_ = function() { | |
| 621 this.container_.style.marginTop = '0px'; | |
| 622 this.container_.style.marginLeft = '0px'; | |
| 623 }; | |
| 624 | |
| 625 /** | |
| 626 * @param {Event} event The mouse event. | |
| 627 * @private | |
| 628 */ | |
| 629 remoting.DesktopConnectedView.prototype.onMouseMove_ = function(event) { | |
| 630 if (this.bumpScrollTimer_) { | |
| 631 window.clearTimeout(this.bumpScrollTimer_); | |
| 632 this.bumpScrollTimer_ = null; | |
| 633 } | |
| 634 | |
| 635 /** | |
| 636 * Compute the scroll speed based on how close the mouse is to the edge. | |
| 637 * @param {number} mousePos The mouse x- or y-coordinate | |
| 638 * @param {number} size The width or height of the content area. | |
| 639 * @return {number} The scroll delta, in pixels. | |
| 640 */ | |
| 641 var computeDelta = function(mousePos, size) { | |
| 642 var threshold = 10; | |
| 643 if (mousePos >= size - threshold) { | |
| 644 return 1 + 5 * (mousePos - (size - threshold)) / threshold; | |
| 645 } else if (mousePos <= threshold) { | |
| 646 return -1 - 5 * (threshold - mousePos) / threshold; | |
| 647 } | |
| 648 return 0; | |
| 649 }; | |
| 650 | |
| 651 var clientArea = this.getClientArea_(); | |
| 652 var dx = computeDelta(event.x, clientArea.width); | |
| 653 var dy = computeDelta(event.y, clientArea.height); | |
| 654 | |
| 655 if (dx != 0 || dy != 0) { | |
| 656 this.raiseEvent(remoting.DesktopConnectedView.Events.bumpScrollStarted); | |
| 657 /** @type {remoting.DesktopConnectedView} */ | |
| 658 var that = this; | |
| 659 /** | |
| 660 * Scroll the view, and schedule a timer to do so again unless we've hit | |
| 661 * the edges of the screen. This timer is cancelled when the mouse moves. | |
| 662 * @param {number} expected The time at which we expect to be called. | |
| 663 */ | |
| 664 var repeatScroll = function(expected) { | |
| 665 /** @type {number} */ | |
| 666 var now = new Date().getTime(); | |
| 667 /** @type {number} */ | |
| 668 var timeout = 10; | |
| 669 var lateAdjustment = 1 + (now - expected) / timeout; | |
| 670 if (that.scroll_(lateAdjustment * dx, lateAdjustment * dy)) { | |
| 671 that.raiseEvent(remoting.DesktopConnectedView.Events.bumpScrollStopped); | |
| 672 } else { | |
| 673 that.bumpScrollTimer_ = window.setTimeout( | |
| 674 function() { repeatScroll(now + timeout); }, | |
| 675 timeout); | |
| 676 } | |
| 677 }; | |
| 678 repeatScroll(new Date().getTime()); | |
| 679 } | |
| 680 }; | |
| 681 | |
| 682 /** | |
| 683 * Scroll the client plugin by the specified amount, keeping it visible. | |
| 684 * Note that this is only used in content full-screen mode (not windowed or | |
| 685 * browser full-screen modes), where window.scrollBy and the scrollTop and | |
| 686 * scrollLeft properties don't work. | |
| 687 * @param {number} dx The amount by which to scroll horizontally. Positive to | |
| 688 * scroll right; negative to scroll left. | |
| 689 * @param {number} dy The amount by which to scroll vertically. Positive to | |
| 690 * scroll down; negative to scroll up. | |
| 691 * @return {boolean} True if the requested scroll had no effect because both | |
| 692 * vertical and horizontal edges of the screen have been reached. | |
| 693 * @private | |
| 694 */ | |
| 695 remoting.DesktopConnectedView.prototype.scroll_ = function(dx, dy) { | |
| 696 /** | |
| 697 * Helper function for x- and y-scrolling | |
| 698 * @param {number|string} curr The current margin, eg. "10px". | |
| 699 * @param {number} delta The requested scroll amount. | |
| 700 * @param {number} windowBound The size of the window, in pixels. | |
| 701 * @param {number} pluginBound The size of the plugin, in pixels. | |
| 702 * @param {{stop: boolean}} stop Reference parameter used to indicate when | |
| 703 * the scroll has reached one of the edges and can be stopped in that | |
| 704 * direction. | |
| 705 * @return {string} The new margin value. | |
| 706 */ | |
| 707 var adjustMargin = function(curr, delta, windowBound, pluginBound, stop) { | |
| 708 var minMargin = Math.min(0, windowBound - pluginBound); | |
| 709 var result = (curr ? parseFloat(curr) : 0) - delta; | |
| 710 result = Math.min(0, Math.max(minMargin, result)); | |
| 711 stop.stop = (result == 0 || result == minMargin); | |
| 712 return result + 'px'; | |
| 713 }; | |
| 714 | |
| 715 var plugin = this.plugin_.element(); | |
| 716 var style = this.container_.style; | |
| 717 | |
| 718 var stopX = { stop: false }; | |
| 719 var clientArea = this.getClientArea_(); | |
| 720 style.marginLeft = adjustMargin(style.marginLeft, dx, clientArea.width, | |
| 721 this.pluginWidthForBumpScrollTesting || plugin.clientWidth, stopX); | |
| 722 | |
| 723 var stopY = { stop: false }; | |
| 724 style.marginTop = adjustMargin( | |
| 725 style.marginTop, dy, clientArea.height, | |
| 726 this.pluginHeightForBumpScrollTesting || plugin.clientHeight, stopY); | |
| 727 return stopX.stop && stopY.stop; | |
| 728 }; | |
| 729 | |
| 730 /** | |
| 731 * Refreshes the plugin's dimensions, taking into account the sizes of the | |
| 732 * remote desktop and client window, and the current scale-to-fit setting. | |
| 733 * | |
| 734 * @return {void} Nothing. | |
| 735 */ | |
| 736 remoting.DesktopConnectedView.prototype.updateDimensions = function() { | |
| 737 if (this.plugin_.getDesktopWidth() == 0 || | |
| 738 this.plugin_.getDesktopHeight() == 0) { | |
| 739 return; | |
| 740 } | |
| 741 | |
| 742 var desktopSize = { width: this.plugin_.getDesktopWidth(), | |
| 743 height: this.plugin_.getDesktopHeight() }; | |
| 744 var desktopDpi = { x: this.plugin_.getDesktopXDpi(), | |
| 745 y: this.plugin_.getDesktopYDpi() }; | |
| 746 var newSize = remoting.DesktopConnectedView.choosePluginSize( | |
| 747 this.getClientArea_(), window.devicePixelRatio, | |
| 748 desktopSize, desktopDpi, this.desktopScale_, | |
| 749 remoting.fullscreen.isActive(), this.shrinkToFit_); | |
| 750 | |
| 751 // Resize the plugin if necessary. | |
| 752 console.log('plugin dimensions:' + newSize.width + 'x' + newSize.height); | |
| 753 this.plugin_.element().style.width = newSize.width + 'px'; | |
| 754 this.plugin_.element().style.height = newSize.height + 'px'; | |
| 755 | |
| 756 // When we receive the first plugin dimensions from the host, we know that | |
| 757 // remote host has started. | |
| 758 remoting.app.onVideoStreamingStarted(); | |
| 759 } | |
| 760 | |
| 761 /** | |
| 762 * Helper function accepting client and host dimensions, and returning a chosen | |
| 763 * size for the plugin element, in DIPs. | |
| 764 * | |
| 765 * @param {{width: number, height: number}} clientSizeDips Available client | |
| 766 * dimensions, in DIPs. | |
| 767 * @param {number} clientPixelRatio Number of physical pixels per client DIP. | |
| 768 * @param {{width: number, height: number}} desktopSize Size of the host desktop | |
| 769 * in physical pixels. | |
| 770 * @param {{x: number, y: number}} desktopDpi DPI of the host desktop in both | |
| 771 * dimensions. | |
| 772 * @param {number} desktopScale The scale factor configured for the host. | |
| 773 * @param {boolean} isFullscreen True if full-screen mode is active. | |
| 774 * @param {boolean} shrinkToFit True if shrink-to-fit should be applied. | |
| 775 * @return {{width: number, height: number}} Chosen plugin dimensions, in DIPs. | |
| 776 */ | |
| 777 remoting.DesktopConnectedView.choosePluginSize = function( | |
| 778 clientSizeDips, clientPixelRatio, desktopSize, desktopDpi, desktopScale, | |
| 779 isFullscreen, shrinkToFit) { | |
| 780 base.debug.assert(clientSizeDips.width > 0); | |
| 781 base.debug.assert(clientSizeDips.height > 0); | |
| 782 base.debug.assert(clientPixelRatio >= 1.0); | |
| 783 base.debug.assert(desktopSize.width > 0); | |
| 784 base.debug.assert(desktopSize.height > 0); | |
| 785 base.debug.assert(desktopDpi.x > 0); | |
| 786 base.debug.assert(desktopDpi.y > 0); | |
| 787 base.debug.assert(desktopScale > 0); | |
| 788 | |
| 789 // We have the following goals in sizing the desktop display at the client: | |
| 790 // 1. Avoid losing detail by down-scaling beyond 1:1 host:device pixels. | |
| 791 // 2. Avoid up-scaling if that will cause the client to need scrollbars. | |
| 792 // 3. Avoid introducing blurriness with non-integer up-scaling factors. | |
| 793 // 4. Avoid having huge "letterboxes" around the desktop, if it's really | |
| 794 // small. | |
| 795 // 5. Compensate for mismatched DPIs, so that the behaviour of features like | |
| 796 // shrink-to-fit matches their "natural" rather than their pixel size. | |
| 797 // e.g. with shrink-to-fit active a 1024x768 low-DPI host on a 640x480 | |
| 798 // high-DPI client will be up-scaled to 1280x960, rather than displayed | |
| 799 // at 1:1 host:physical client pixels. | |
| 800 // | |
| 801 // To determine the ideal size we follow a four-stage process: | |
| 802 // 1. Determine the "natural" size at which to display the desktop. | |
| 803 // a. Initially assume 1:1 mapping of desktop to client device pixels. | |
| 804 // b. If host DPI is less than the client's then up-scale accordingly. | |
| 805 // c. If desktopScale is configured for the host then allow that to | |
| 806 // reduce the amount of up-scaling from (b). e.g. if the client:host | |
| 807 // DPIs are 2:1 then a desktopScale of 1.5 would reduce the up-scale | |
| 808 // to 4:3, while a desktopScale of 3.0 would result in no up-scaling. | |
| 809 // 2. If the natural size of the desktop is smaller than the client device | |
| 810 // then apply up-scaling by an integer scale factor to avoid excessive | |
| 811 // letterboxing. | |
| 812 // 3. If shrink-to-fit is configured then: | |
| 813 // a. If the natural size exceeds the client size then apply down-scaling | |
| 814 // by an arbitrary scale factor. | |
| 815 // b. If we're in full-screen mode and the client & host aspect-ratios | |
| 816 // are radically different (e.g. the host is actually multi-monitor) | |
| 817 // then shrink-to-fit to the shorter dimension, rather than leaving | |
| 818 // huge letterboxes; the user can then bump-scroll around the desktop. | |
| 819 // 4. If the overall scale factor is fractionally over an integer factor | |
| 820 // then reduce it to that integer factor, to avoid blurring. | |
| 821 | |
| 822 // All calculations are performed in device pixels. | |
| 823 var clientWidth = clientSizeDips.width * clientPixelRatio; | |
| 824 var clientHeight = clientSizeDips.height * clientPixelRatio; | |
| 825 | |
| 826 // 1. Determine a "natural" size at which to display the desktop. | |
| 827 var scale = 1.0; | |
| 828 | |
| 829 // Determine the effective host device pixel ratio. | |
| 830 // Note that we round up or down to the closest integer pixel ratio. | |
| 831 var hostPixelRatioX = Math.round(desktopDpi.x / 96); | |
| 832 var hostPixelRatioY = Math.round(desktopDpi.y / 96); | |
| 833 var hostPixelRatio = Math.min(hostPixelRatioX, hostPixelRatioY); | |
| 834 | |
| 835 // Allow up-scaling to account for DPI. | |
| 836 scale = Math.max(scale, clientPixelRatio / hostPixelRatio); | |
| 837 | |
| 838 // Allow some or all of the up-scaling to be cancelled by the desktopScale. | |
| 839 if (desktopScale > 1.0) { | |
| 840 scale = Math.max(1.0, scale / desktopScale); | |
| 841 } | |
| 842 | |
| 843 // 2. If the host is still much smaller than the client, then up-scale to | |
| 844 // avoid wasting space, but only by an integer factor, to avoid blurring. | |
| 845 if (desktopSize.width * scale <= clientWidth && | |
| 846 desktopSize.height * scale <= clientHeight) { | |
| 847 var scaleX = Math.floor(clientWidth / desktopSize.width); | |
| 848 var scaleY = Math.floor(clientHeight / desktopSize.height); | |
| 849 scale = Math.min(scaleX, scaleY); | |
| 850 base.debug.assert(scale >= 1.0); | |
| 851 } | |
| 852 | |
| 853 // 3. Apply shrink-to-fit, if configured. | |
| 854 if (shrinkToFit) { | |
| 855 var scaleFitWidth = Math.min(scale, clientWidth / desktopSize.width); | |
| 856 var scaleFitHeight = Math.min(scale, clientHeight / desktopSize.height); | |
| 857 scale = Math.min(scaleFitHeight, scaleFitWidth); | |
| 858 | |
| 859 // If we're running full-screen then try to handle common side-by-side | |
| 860 // multi-monitor combinations more intelligently. | |
| 861 if (isFullscreen) { | |
| 862 // If the host has two monitors each the same size as the client then | |
| 863 // scale-to-fit will have the desktop occupy only 50% of the client area, | |
| 864 // in which case it would be preferable to down-scale less and let the | |
| 865 // user bump-scroll around ("scale-and-pan"). | |
| 866 // Triggering scale-and-pan if less than 65% of the client area would be | |
| 867 // used adds enough fuzz to cope with e.g. 1280x800 client connecting to | |
| 868 // a (2x1280)x1024 host nicely. | |
| 869 // Note that we don't need to account for scrollbars while fullscreen. | |
| 870 if (scale <= scaleFitHeight * 0.65) { | |
| 871 scale = scaleFitHeight; | |
| 872 } | |
| 873 if (scale <= scaleFitWidth * 0.65) { | |
| 874 scale = scaleFitWidth; | |
| 875 } | |
| 876 } | |
| 877 } | |
| 878 | |
| 879 // 4. Avoid blurring for close-to-integer up-scaling factors. | |
| 880 if (scale > 1.0) { | |
| 881 var scaleBlurriness = scale / Math.floor(scale); | |
| 882 if (scaleBlurriness < 1.1) { | |
| 883 scale = Math.floor(scale); | |
| 884 } | |
| 885 } | |
| 886 | |
| 887 // Return the necessary plugin dimensions in DIPs. | |
| 888 scale = scale / clientPixelRatio; | |
| 889 var pluginWidth = Math.round(desktopSize.width * scale); | |
| 890 var pluginHeight = Math.round(desktopSize.height * scale); | |
| 891 return { width: pluginWidth, height: pluginHeight }; | |
| 892 } | |
| 893 | |
| 894 /** | |
| 895 * Called when the window or desktop size or the scaling settings change, | |
| 896 * to set the scroll-bar visibility. | |
| 897 * | |
| 898 * TODO(jamiewalch): crbug.com/252796: Remove this once crbug.com/240772 is | |
| 899 * fixed. | |
| 900 */ | |
| 901 remoting.DesktopConnectedView.prototype.updateScrollbarVisibility = function() { | |
| 902 var scroller = document.getElementById('scroller'); | |
| 903 if (!scroller) { | |
| 904 return; | |
| 905 } | |
| 906 | |
| 907 var needsVerticalScroll = false; | |
| 908 var needsHorizontalScroll = false; | |
| 909 if (!this.shrinkToFit_) { | |
| 910 // Determine whether or not horizontal or vertical scrollbars are | |
| 911 // required, taking into account their width. | |
| 912 var clientArea = this.getClientArea_(); | |
| 913 needsVerticalScroll = clientArea.height < this.plugin_.getDesktopHeight(); | |
| 914 needsHorizontalScroll = clientArea.width < this.plugin_.getDesktopWidth(); | |
| 915 var kScrollBarWidth = 16; | |
| 916 if (needsHorizontalScroll && !needsVerticalScroll) { | |
| 917 needsVerticalScroll = | |
| 918 clientArea.height - kScrollBarWidth < this.plugin_.getDesktopHeight(); | |
| 919 } else if (!needsHorizontalScroll && needsVerticalScroll) { | |
| 920 needsHorizontalScroll = | |
| 921 clientArea.width - kScrollBarWidth < this.plugin_.getDesktopWidth(); | |
| 922 } | |
| 923 } | |
| 924 | |
| 925 if (needsHorizontalScroll) { | |
| 926 scroller.classList.remove('no-horizontal-scroll'); | |
| 927 } else { | |
| 928 scroller.classList.add('no-horizontal-scroll'); | |
| 929 } | |
| 930 if (needsVerticalScroll) { | |
| 931 scroller.classList.remove('no-vertical-scroll'); | |
| 932 } else { | |
| 933 scroller.classList.add('no-vertical-scroll'); | |
| 934 } | |
| 935 }; | |
| 936 | |
| 937 /** | |
| 938 * Sets and stores the key remapping setting for the current host. | 411 * Sets and stores the key remapping setting for the current host. |
| 939 * | 412 * |
| 940 * @param {string} remappings Comma separated list of key remappings. | 413 * @param {string} remappings Comma separated list of key remappings. |
| 941 */ | 414 */ |
| 942 remoting.DesktopConnectedView.prototype.setRemapKeys = function(remappings) { | 415 remoting.DesktopConnectedView.prototype.setRemapKeys = function(remappings) { |
| 943 // Cancel any existing remappings and apply the new ones. | 416 // Cancel any existing remappings and apply the new ones. |
| 944 this.applyRemapKeys_(false); | 417 this.applyRemapKeys_(false); |
| 945 this.remapKeys_ = remappings; | 418 this.remapKeys_ = remappings; |
| 946 this.applyRemapKeys_(true); | 419 this.applyRemapKeys_(true); |
| 947 | 420 |
| 948 // Save the new remapping setting. | 421 // Save the new remapping setting. |
| 949 var options = {}; | 422 this.host_.options().remapKeys = this.remapKeys_; |
| 950 options[remoting.DesktopConnectedView.KEY_REMAP_KEYS] = this.remapKeys_; | 423 this.host_.options().save(); |
| 951 remoting.HostSettings.save(this.hostId_, options); | |
| 952 }; | 424 }; |
| 953 | 425 |
| 954 /** | 426 /** |
| 955 * Applies the configured key remappings to the session, or resets them. | 427 * Applies the configured key remappings to the session, or resets them. |
| 956 * | 428 * |
| 957 * @param {boolean} apply True to apply remappings, false to cancel them. | 429 * @param {boolean} apply True to apply remappings, false to cancel them. |
| 958 */ | 430 */ |
| 959 remoting.DesktopConnectedView.prototype.applyRemapKeys_ = function(apply) { | 431 remoting.DesktopConnectedView.prototype.applyRemapKeys_ = function(apply) { |
| 960 var remapKeys = this.remapKeys_; | 432 var remapKeys = this.remapKeys_; |
| 961 if (remapKeys == '') { | 433 if (remapKeys == '') { |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1088 * @param {Object} message The parsed extension message data. | 560 * @param {Object} message The parsed extension message data. |
| 1089 * @return {boolean} True if the message was recognized, false otherwise. | 561 * @return {boolean} True if the message was recognized, false otherwise. |
| 1090 */ | 562 */ |
| 1091 remoting.DesktopConnectedView.prototype.handleExtensionMessage = | 563 remoting.DesktopConnectedView.prototype.handleExtensionMessage = |
| 1092 function(type, message) { | 564 function(type, message) { |
| 1093 if (this.videoFrameRecorder_) { | 565 if (this.videoFrameRecorder_) { |
| 1094 return this.videoFrameRecorder_.handleMessage(type, message); | 566 return this.videoFrameRecorder_.handleMessage(type, message); |
| 1095 } | 567 } |
| 1096 return false; | 568 return false; |
| 1097 }; | 569 }; |
| OLD | NEW |