diff --git a/app/ui.js b/app/ui.js index 7207b2ffd..a4b604fa5 100644 --- a/app/ui.js +++ b/app/ui.js @@ -159,6 +159,7 @@ const UI = { UI.initSetting('host', window.location.hostname); UI.initSetting('port', port); UI.initSetting('encrypt', (window.location.protocol === "https:")); + UI.initSetting('native_dpi', false); UI.initSetting('view_clip', false); UI.initSetting('resize', 'off'); UI.initSetting('quality', 6); @@ -355,6 +356,8 @@ const UI = { UI.addSettingChangeHandler('compression', UI.updateCompression); UI.addSettingChangeHandler('view_clip'); UI.addSettingChangeHandler('view_clip', UI.updateViewClip); + UI.addSettingChangeHandler('native_dpi'); + UI.addSettingChangeHandler('native_dpi', UI.updateNativeDpi); UI.addSettingChangeHandler('shared'); UI.addSettingChangeHandler('view_only'); UI.addSettingChangeHandler('view_only', UI.updateViewOnly); @@ -843,6 +846,7 @@ const UI = { // Refresh UI elements from saved cookies UI.updateSetting('encrypt'); + UI.updateSetting('native_dpi'); UI.updateSetting('view_clip'); UI.updateSetting('resize'); UI.updateSetting('quality'); @@ -1044,6 +1048,7 @@ const UI = { UI.rfb.addEventListener("clipboard", UI.clipboardReceive); UI.rfb.addEventListener("bell", UI.bell); UI.rfb.addEventListener("desktopname", UI.updateDesktopName); + UI.rfb.useNativeDpi = UI.getSetting('native_dpi'); UI.rfb.clipViewport = UI.getSetting('view_clip'); UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale'; UI.rfb.resizeSession = UI.getSetting('resize') === 'remote'; @@ -1305,6 +1310,31 @@ const UI = { /* ------^------- * /VIEW CLIPPING * ============== + * NATIVE DPI + * ------v------*/ + + // Update native DPI scaling for the viewport. If enabled, a pixel on the screen will + // correspond to one pixel on the server framebuffer. + // If disabled, the client area will be scaled according to the DPI scaling applied by the OS + // When this is disabled on a high-DPI monitor, a smaller framebuffer will be used and the + // result scaled up to fit the client area + updateNativeDpi() { + if (!UI.rfb) return; + + const scaling = UI.getSetting('resize') === 'scale'; + + if (scaling) { + // Can't control DPI if viewport is scaled to fit + UI.forceSetting('native_dpi', false); + } else { + UI.enableSetting('native_dpi'); + } + UI.rfb.useNativeDpi = UI.getSetting('native_dpi'); + }, + +/* ------^------- + * /NATIVE DPI + * ============== * VIEWDRAG * ------v------*/ diff --git a/core/rfb.js b/core/rfb.js index 07d5bc060..435afb486 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -337,6 +337,16 @@ export default class RFB extends EventTargetMixin { } } + get useNativeDpi() { return this._nativeDpi; } + set useNativeDpi(nativeDpi) { + this._nativeDpi = nativeDpi; + this._updateScale(); + this._updateClip(); + if (this._resizeSession) { + this._requestRemoteResize(); + } + } + get showDotCursor() { return this._showDotCursor; } set showDotCursor(show) { this._showDotCursor = show; @@ -604,7 +614,14 @@ export default class RFB extends EventTargetMixin { _updateScale() { if (!this._scaleViewport) { - this._display.scale = 1.0; + if (this._nativeDpi) { + const dpr = window.devicePixelRatio || 1; + const scaledWidth = Math.floor(window.innerWidth * dpr); + // Compute the exact scale factor to match the floored width/height + this._display.scale = window.innerWidth / scaledWidth; + } else { + this._display.scale = 1; + } } else { const size = this._screenSize(); this._display.autoscale(size.w, size.h); @@ -624,8 +641,10 @@ export default class RFB extends EventTargetMixin { } const size = this._screenSize(); + size.w = Math.floor(size.w / this._display.scale); + size.h = Math.floor(size.h / this._display.scale); RFB.messages.setDesktopSize(this._sock, - Math.floor(size.w), Math.floor(size.h), + size.w, size.h, this._screenID, this._screenFlags); Log.Debug('Requested new desktop size: ' + diff --git a/vnc.html b/vnc.html index 0f2a3b351..6d1538f6a 100644 --- a/vnc.html +++ b/vnc.html @@ -190,6 +190,9 @@