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'; |
(...skipping 721 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
732 * remote desktop and client window, and the current scale-to-fit setting. | 732 * remote desktop and client window, and the current scale-to-fit setting. |
733 * | 733 * |
734 * @return {void} Nothing. | 734 * @return {void} Nothing. |
735 */ | 735 */ |
736 remoting.DesktopConnectedView.prototype.updateDimensions = function() { | 736 remoting.DesktopConnectedView.prototype.updateDimensions = function() { |
737 if (this.plugin_.getDesktopWidth() == 0 || | 737 if (this.plugin_.getDesktopWidth() == 0 || |
738 this.plugin_.getDesktopHeight() == 0) { | 738 this.plugin_.getDesktopHeight() == 0) { |
739 return; | 739 return; |
740 } | 740 } |
741 | 741 |
742 var clientArea = this.getClientArea_(); | 742 var desktopSize = { width: this.plugin_.getDesktopWidth(), |
743 var desktopWidth = this.plugin_.getDesktopWidth(); | 743 height: this.plugin_.getDesktopHeight() }; |
744 var desktopHeight = 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_); |
745 | 750 |
746 // When configured to display a host at its original size, we aim to display | 751 // Resize the plugin if necessary. |
747 // it as close to its physical size as possible, without losing data: | 752 console.log('plugin dimensions:' + newSize.width + 'x' + newSize.height); |
748 // - If client and host have matching DPI, render the host pixel-for-pixel. | 753 this.plugin_.element().style.width = newSize.width + 'px'; |
749 // - If the host has higher DPI then still render pixel-for-pixel. | 754 this.plugin_.element().style.height = newSize.height + 'px'; |
750 // - If the host has lower DPI then let Chrome up-scale it to natural size. | |
751 | 755 |
752 // We specify the plugin dimensions in Density-Independent Pixels, so to | 756 // When we receive the first plugin dimensions from the host, we know that |
753 // render pixel-for-pixel we need to down-scale the host dimensions by the | 757 // remote host has started. |
754 // devicePixelRatio of the client. To match the host pixel density, we choose | 758 remoting.app.onVideoStreamingStarted(); |
755 // an initial scale factor based on the client devicePixelRatio and host DPI. | 759 } |
756 | 760 |
757 // Determine the effective device pixel ratio of the host, based on DPI. | 761 /** |
758 var hostPixelRatioX = Math.ceil(this.plugin_.getDesktopXDpi() / 96); | 762 * Helper function accepting client and host dimensions, and returning a chosen |
759 var hostPixelRatioY = Math.ceil(this.plugin_.getDesktopYDpi() / 96); | 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); |
760 var hostPixelRatio = Math.min(hostPixelRatioX, hostPixelRatioY); | 833 var hostPixelRatio = Math.min(hostPixelRatioX, hostPixelRatioY); |
761 | 834 |
762 // Include the desktopScale in the hostPixelRatio before comparing it with | 835 // Allow up-scaling to account for DPI. |
763 // the client devicePixelRatio to determine the "natural" scale to use. | 836 scale = Math.max(scale, clientPixelRatio / hostPixelRatio); |
764 hostPixelRatio *= this.desktopScale_; | |
765 | 837 |
766 // Down-scale by the smaller of the client and host ratios. | 838 // Allow some or all of the up-scaling to be cancelled by the desktopScale. |
767 var scale = 1.0 / Math.min(window.devicePixelRatio, hostPixelRatio); | 839 if (desktopScale > 1.0) { |
| 840 scale = Math.max(1.0, scale / desktopScale); |
| 841 } |
768 | 842 |
769 if (this.shrinkToFit_) { | 843 // 2. If the host is still much smaller than the client, then up-scale to |
770 // Reduce the scale, if necessary, to fit the whole desktop in the window. | 844 // avoid wasting space, but only by an integer factor, to avoid blurring. |
771 var scaleFitWidth = Math.min(scale, 1.0 * clientArea.width / desktopWidth); | 845 if (desktopSize.width * scale <= clientWidth && |
772 var scaleFitHeight = | 846 desktopSize.height * scale <= clientHeight) { |
773 Math.min(scale, 1.0 * clientArea.height / desktopHeight); | 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); |
774 scale = Math.min(scaleFitHeight, scaleFitWidth); | 857 scale = Math.min(scaleFitHeight, scaleFitWidth); |
775 | 858 |
776 // If we're running full-screen then try to handle common side-by-side | 859 // If we're running full-screen then try to handle common side-by-side |
777 // multi-monitor combinations more intelligently. | 860 // multi-monitor combinations more intelligently. |
778 if (remoting.fullscreen.isActive()) { | 861 if (isFullscreen) { |
779 // If the host has two monitors each the same size as the client then | 862 // If the host has two monitors each the same size as the client then |
780 // scale-to-fit will have the desktop occupy only 50% of the client area, | 863 // scale-to-fit will have the desktop occupy only 50% of the client area, |
781 // in which case it would be preferable to down-scale less and let the | 864 // in which case it would be preferable to down-scale less and let the |
782 // user bump-scroll around ("scale-and-pan"). | 865 // user bump-scroll around ("scale-and-pan"). |
783 // Triggering scale-and-pan if less than 65% of the client area would be | 866 // Triggering scale-and-pan if less than 65% of the client area would be |
784 // used adds enough fuzz to cope with e.g. 1280x800 client connecting to | 867 // used adds enough fuzz to cope with e.g. 1280x800 client connecting to |
785 // a (2x1280)x1024 host nicely. | 868 // a (2x1280)x1024 host nicely. |
786 // Note that we don't need to account for scrollbars while fullscreen. | 869 // Note that we don't need to account for scrollbars while fullscreen. |
787 if (scale <= scaleFitHeight * 0.65) { | 870 if (scale <= scaleFitHeight * 0.65) { |
788 scale = scaleFitHeight; | 871 scale = scaleFitHeight; |
789 } | 872 } |
790 if (scale <= scaleFitWidth * 0.65) { | 873 if (scale <= scaleFitWidth * 0.65) { |
791 scale = scaleFitWidth; | 874 scale = scaleFitWidth; |
792 } | 875 } |
793 } | 876 } |
794 } | 877 } |
795 | 878 |
796 var pluginWidth = Math.round(desktopWidth * scale); | 879 // 4. Avoid blurring for close-to-integer up-scaling factors. |
797 var pluginHeight = Math.round(desktopHeight * scale); | 880 if (scale > 1.0) { |
| 881 var scaleBlurriness = scale / Math.floor(scale); |
| 882 if (scaleBlurriness < 1.1) { |
| 883 scale = Math.floor(scale); |
| 884 } |
| 885 } |
798 | 886 |
799 // Resize the plugin if necessary. | 887 // Return the necessary plugin dimensions in DIPs. |
800 // TODO(wez): Handle high-DPI to high-DPI properly (crbug.com/135089). | 888 scale = scale / clientPixelRatio; |
801 this.plugin_.element().style.width = pluginWidth + 'px'; | 889 var pluginWidth = Math.round(desktopSize.width * scale); |
802 this.plugin_.element().style.height = pluginHeight + 'px'; | 890 var pluginHeight = Math.round(desktopSize.height * scale); |
803 | 891 return { width: pluginWidth, height: pluginHeight }; |
804 // Position the container. | 892 } |
805 // Note that clientWidth/Height take into account scrollbars. | |
806 var clientWidth = document.documentElement.clientWidth; | |
807 var clientHeight = document.documentElement.clientHeight; | |
808 var parentNode = this.plugin_.element().parentNode; | |
809 | |
810 console.log('plugin dimensions: ' + | |
811 parentNode.style.left + ',' + | |
812 parentNode.style.top + '-' + | |
813 pluginWidth + 'x' + pluginHeight + '.'); | |
814 | |
815 // When we receive the first plugin dimensions from the host, we know that | |
816 // remote host has started. | |
817 remoting.app.onVideoStreamingStarted(); | |
818 }; | |
819 | 893 |
820 /** | 894 /** |
821 * Called when the window or desktop size or the scaling settings change, | 895 * Called when the window or desktop size or the scaling settings change, |
822 * to set the scroll-bar visibility. | 896 * to set the scroll-bar visibility. |
823 * | 897 * |
824 * TODO(jamiewalch): crbug.com/252796: Remove this once crbug.com/240772 is | 898 * TODO(jamiewalch): crbug.com/252796: Remove this once crbug.com/240772 is |
825 * fixed. | 899 * fixed. |
826 */ | 900 */ |
827 remoting.DesktopConnectedView.prototype.updateScrollbarVisibility = function() { | 901 remoting.DesktopConnectedView.prototype.updateScrollbarVisibility = function() { |
828 var scroller = document.getElementById('scroller'); | 902 var scroller = document.getElementById('scroller'); |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1014 * @param {Object} message The parsed extension message data. | 1088 * @param {Object} message The parsed extension message data. |
1015 * @return {boolean} True if the message was recognized, false otherwise. | 1089 * @return {boolean} True if the message was recognized, false otherwise. |
1016 */ | 1090 */ |
1017 remoting.DesktopConnectedView.prototype.handleExtensionMessage = | 1091 remoting.DesktopConnectedView.prototype.handleExtensionMessage = |
1018 function(type, message) { | 1092 function(type, message) { |
1019 if (this.videoFrameRecorder_) { | 1093 if (this.videoFrameRecorder_) { |
1020 return this.videoFrameRecorder_.handleMessage(type, message); | 1094 return this.videoFrameRecorder_.handleMessage(type, message); |
1021 } | 1095 } |
1022 return false; | 1096 return false; |
1023 }; | 1097 }; |
OLD | NEW |