Bugfix/kasm 5453 serverside fixed resolutions 2 (#90)
* fix serverside fixed resolutions * refactor of multi-monitor screen planning * KASM-5488 mouse up fix * Fixed display manager not getting updates on primary scren size, static size fixes * fix phantom screen * fix mouse off on secondary screen when scaled * fix scaling off * fix exception on setting static resolution * completely disable client set static resolution on secondary displays --------- Co-authored-by: mattmcclaskey <matt@kasmweb.com> Co-authored-by: Chris Hunt <chris.hunt@kasmweb.com>
This commit is contained in:
parent
f6c1d8c668
commit
4825e12f62
23
app/ui.js
23
app/ui.js
|
@ -1745,10 +1745,9 @@ const UI = {
|
|||
if (UI.rfb) {
|
||||
UI.rfb.forcedResolutionX = event.data.value_x;
|
||||
UI.rfb.forcedResolutionY = event.data.value_y;
|
||||
UI.forceSetting('forced_resolution_x', event.data.value_x, false);
|
||||
UI.forceSetting('forced_resolution_y', event.data.value_y, false);
|
||||
UI.applyResizeMode();
|
||||
UI.rfb.forcedResolutionX = null;
|
||||
UI.rfb.forcedResolutionY = null;
|
||||
UI.rfb._resizeSession = UI.getSetting('resize') === 'remote';
|
||||
}
|
||||
break;
|
||||
case 'set_perf_stats':
|
||||
|
@ -1861,13 +1860,24 @@ const UI = {
|
|||
// Apply remote resizing or local scaling
|
||||
applyResizeMode() {
|
||||
if (!UI.rfb) return;
|
||||
|
||||
UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
|
||||
UI.rfb.resizeSession = UI.getSetting('resize') === 'remote' || UI.rfb.forcedResolutionX && UI.rfb.forcedResolutionY;
|
||||
const resize_setting = UI.getSetting('resize');
|
||||
UI.rfb.clipViewport = resize_setting !== 'off';
|
||||
UI.rfb.scaleViewport = resize_setting === 'scale';
|
||||
UI.rfb.resizeSession = resize_setting === 'remote';
|
||||
UI.rfb.idleDisconnect = UI.getSetting('idle_disconnect');
|
||||
UI.rfb.videoQuality = UI.getSetting('video_quality');
|
||||
UI.rfb.enableWebP = UI.getSetting('enable_webp');
|
||||
UI.rfb.enableHiDpi = UI.getSetting('enable_hidpi');
|
||||
|
||||
if (UI.rfb.resizeSession) {
|
||||
UI.rfb.forcedResolutionX = null;
|
||||
UI.rfb.forcedResolutionY = null;
|
||||
} else {
|
||||
UI.rfb.forcedResolutionX = UI.getSetting('forced_resolution_x', false);
|
||||
UI.rfb.forcedResolutionY = UI.getSetting('forced_resolution_y', false);
|
||||
}
|
||||
|
||||
UI.rfb.updateConnectionSettings();
|
||||
},
|
||||
|
||||
/* ------^-------
|
||||
|
@ -2582,6 +2592,7 @@ const UI = {
|
|||
} else {
|
||||
UI.rfb.enableHiDpi = false;
|
||||
}
|
||||
UI.applyResizeMode();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -142,7 +142,8 @@ const UI = {
|
|||
}
|
||||
}
|
||||
|
||||
UI.rfb = new RFB(document.getElementById('noVNC_container'),
|
||||
if (!UI.rfb) {
|
||||
UI.rfb = new RFB(document.getElementById('noVNC_container'),
|
||||
document.getElementById('noVNC_keyboardinput'),
|
||||
"", //URL
|
||||
{
|
||||
|
@ -153,11 +154,18 @@ const UI = {
|
|||
},
|
||||
false // Not a primary display
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
UI.rfb.addEventListener("connect", UI.connectFinished);
|
||||
//UI.rfb.addEventListener("disconnect", UI.disconnectFinished);
|
||||
UI.rfb.clipViewport = UI.getSetting('view_clip');
|
||||
UI.rfb.scaleViewport = UI.getSetting('resize', false, 'remote') === 'scale';
|
||||
UI.rfb.resizeSession = UI.getSetting('resize', false, 'remote') === 'remote';
|
||||
//TODO: add support for forced static resolution for multiple monitors
|
||||
//UI.rfb.forcedResolutionX = UI.getSetting('forced_resolution_x', false);
|
||||
//UI.rfb.forcedResolutionY = UI.getSetting('forced_resolution_y', false);
|
||||
const resize_setting = UI.getSetting('resize', false, 'remote');
|
||||
UI.rfb.clipViewport = resize_setting !== 'off';
|
||||
UI.rfb.scaleViewport = resize_setting === 'scale';
|
||||
UI.rfb.resizeSession = resize_setting === 'remote';
|
||||
UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
|
||||
UI.rfb.dynamicQualityMin = parseInt(UI.getSetting('dynamic_quality_min'));
|
||||
UI.rfb.dynamicQualityMax = parseInt(UI.getSetting('dynamic_quality_max'));
|
||||
|
@ -194,13 +202,9 @@ const UI = {
|
|||
}
|
||||
|
||||
//attach this secondary display to the primary display
|
||||
if (UI.screenID === null) {
|
||||
const screen = UI.rfb.attachSecondaryDisplay(details);
|
||||
UI.screenID = screen.screenID
|
||||
UI.screen = screen
|
||||
} else {
|
||||
UI.rfb.reattachSecondaryDisplay(UI.screen, details);
|
||||
}
|
||||
const screen = UI.rfb.attachSecondaryDisplay(details);
|
||||
UI.screenID = screen.screenID
|
||||
UI.screen = screen
|
||||
document.querySelector('title').textContent = 'Display ' + UI.screenID
|
||||
|
||||
|
||||
|
@ -406,6 +410,19 @@ const UI = {
|
|||
return val;
|
||||
},
|
||||
|
||||
// Apply remote resizing or local scaling
|
||||
applyResizeMode() {
|
||||
if (!UI.rfb) return;
|
||||
const resize_setting = UI.getSetting('resize');
|
||||
UI.rfb.clipViewport = resize_setting !== 'off';
|
||||
UI.rfb.scaleViewport = resize_setting === 'scale';
|
||||
UI.rfb.resizeSession = resize_setting === 'remote' || UI.rfb.forcedResolutionX && UI.rfb.forcedResolutionY;
|
||||
UI.rfb.idleDisconnect = UI.getSetting('idle_disconnect');
|
||||
UI.rfb.videoQuality = UI.getSetting('video_quality');
|
||||
UI.rfb.enableWebP = UI.getSetting('enable_webp');
|
||||
UI.rfb.enableHiDpi = UI.getSetting('enable_hidpi');
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
UI.prime();
|
||||
|
|
203
core/display.js
203
core/display.js
|
@ -96,15 +96,20 @@ export default class Display {
|
|||
height: this._target.height, //client
|
||||
serverWidth: 0, //calculated
|
||||
serverHeight: 0, //calculated
|
||||
serverReportedWidth: 0,
|
||||
serverReportedHeight: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
scale: 1,
|
||||
relativePosition: 0, //left, right, up, down relative to primary display
|
||||
relativePositionX: 0, //offset relative to primary monitor, always 0 for primary
|
||||
relativePositionY: 0, //offset relative to primary monitor, always 0 for primary
|
||||
pixelRatio: window.devicePixelRatio,
|
||||
containerHeight: this._target.parentNode.offsetHeight,
|
||||
containerWidth: this._target.parentNode.offsetWidth,
|
||||
channel: null
|
||||
channel: null,
|
||||
x2: 0,
|
||||
y2: 0
|
||||
}];
|
||||
|
||||
//optional offscreen canvas
|
||||
|
@ -224,75 +229,87 @@ export default class Display {
|
|||
*/
|
||||
getServerRelativeCoordinates(screenIndex, x, y) {
|
||||
if (screenIndex >= 0 && screenIndex < this._screens.length) {
|
||||
x += this._screens[screenIndex].x;
|
||||
y += this._screens[screenIndex].y;
|
||||
x = toSigned32bit(x / this._screens[screenIndex].scale + this._screens[screenIndex].x);
|
||||
y = toSigned32bit(y / this._screens[screenIndex].scale + this._screens[screenIndex].y);
|
||||
}
|
||||
|
||||
return [x, y];
|
||||
}
|
||||
|
||||
getScreenSize(resolutionQuality, max_width, max_height, hiDpi, disableLimit) {
|
||||
getScreenSize(resolutionQuality, max_width, max_height, hiDpi, disableLimit, disableScaling) {
|
||||
let data = {
|
||||
screens: null,
|
||||
serverWidth: 0,
|
||||
serverHeight: 0
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
|
||||
//recalculate primary display container size
|
||||
this._screens[0].containerHeight = this._target.parentNode.offsetHeight;
|
||||
this._screens[0].containerWidth = this._target.parentNode.offsetWidth;
|
||||
this._screens[0].pixelRatio = window.devicePixelRatio;
|
||||
this._screens[0].width = this._target.parentNode.offsetWidth;
|
||||
this._screens[0].height = this._target.parentNode.offsetHeight;
|
||||
this._screens[i].containerHeight = this._target.parentNode.offsetHeight;
|
||||
this._screens[i].containerWidth = this._target.parentNode.offsetWidth;
|
||||
this._screens[i].pixelRatio = window.devicePixelRatio;
|
||||
this._screens[i].width = this._target.parentNode.offsetWidth;
|
||||
this._screens[i].height = this._target.parentNode.offsetHeight;
|
||||
|
||||
//calculate server-side and client-side resolution of each screen
|
||||
for (let i=0; i<this._screens.length; i++) {
|
||||
let width = max_width || this._screens[i].containerWidth;
|
||||
let height = max_height || this._screens[i].containerHeight;
|
||||
let scale = 0;
|
||||
let width = max_width || this._screens[i].containerWidth;
|
||||
let height = max_height || this._screens[i].containerHeight;
|
||||
let scale = 1;
|
||||
|
||||
//max the resolution of a single screen to 1280
|
||||
if (width > 1280 && !disableLimit && resolutionQuality == 1) {
|
||||
height = 1280 * (height/width); //keeping the aspect ratio of original resolution, shrink y to match x
|
||||
width = 1280;
|
||||
}
|
||||
//hard coded 720p
|
||||
else if (resolutionQuality == 0 && !disableLimit) {
|
||||
width = 1280;
|
||||
height = 720;
|
||||
}
|
||||
//force full resolution on a high DPI monitor where the OS is scaling
|
||||
else if (hiDpi) {
|
||||
width = width * this._screens[i].pixelRatio;
|
||||
height = height * this._screens[i].pixelRatio;
|
||||
scale = 1 / this._screens[i].pixelRatio;
|
||||
}
|
||||
//physically small device with high DPI
|
||||
else if (this._antiAliasing === 0 && this._screens[i].pixelRatio > 1 && width < 1000 & width > 0) {
|
||||
Log.Info('Device Pixel ratio: ' + this._screens[i].pixelRatio + ' Reported Resolution: ' + width + 'x' + height);
|
||||
let targetDevicePixelRatio = 1.5;
|
||||
if (this._screens[i].pixelRatio > 2) { targetDevicePixelRatio = 2; }
|
||||
let scaledWidth = (width * this._screens[i].pixelRatio) * (1 / targetDevicePixelRatio);
|
||||
let scaleRatio = scaledWidth / width;
|
||||
width = width * scaleRatio;
|
||||
height = height * scaleRatio;
|
||||
scale = 1 / scaleRatio;
|
||||
Log.Info('Small device with hDPI screen detected, auto scaling at ' + scaleRatio + ' to ' + width + 'x' + height);
|
||||
}
|
||||
|
||||
let clientServerRatioH = this._screens[i].containerHeight / height;
|
||||
let clientServerRatioW = this._screens[i].containerWidth / width;
|
||||
|
||||
this._screens[i].height = Math.floor(height * clientServerRatioH);
|
||||
this._screens[i].width = Math.floor(width * clientServerRatioW);
|
||||
this._screens[i].serverWidth = width;
|
||||
this._screens[i].serverHeight = height;
|
||||
this._screens[i].scale = scale;
|
||||
//max the resolution of a single screen to 1280
|
||||
if (
|
||||
(this._screens[i].serverReportedWidth > 0 && this._screens[i].serverReportedHeight > 0) &&
|
||||
(
|
||||
disableScaling ||
|
||||
(this._screens[i].serverReportedWidth !== this._screens[i].serverWidth || this._screens[i].serverReportedHeight !== this._screens[i].serverHeight)
|
||||
) &&
|
||||
(!max_width && !max_height)
|
||||
) {
|
||||
height = this._screens[i].serverReportedHeight;
|
||||
width = this._screens[i].serverReportedWidth;
|
||||
}
|
||||
else if (width > 1280 && !disableLimit && resolutionQuality == 1) {
|
||||
height = Math.floor(1280 * (height/width)); //keeping the aspect ratio of original resolution, shrink y to match x
|
||||
width = 1280;
|
||||
}
|
||||
//hard coded 720p
|
||||
else if (resolutionQuality == 0 && !disableLimit) {
|
||||
width = 1280;
|
||||
height = 720;
|
||||
}
|
||||
//force full resolution on a high DPI monitor where the OS is scaling
|
||||
else if (hiDpi) {
|
||||
width = Math.floor(width * this._screens[i].pixelRatio);
|
||||
height = Math.floor(height * this._screens[i].pixelRatio);
|
||||
scale = 1 / this._screens[i].pixelRatio;
|
||||
}
|
||||
//physically small device with high DPI
|
||||
else if (this._antiAliasing === 0 && this._screens[i].pixelRatio > 1 && width < 1000 & width > 0) {
|
||||
Log.Info('Device Pixel ratio: ' + this._screens[i].pixelRatio + ' Reported Resolution: ' + width + 'x' + height);
|
||||
let targetDevicePixelRatio = 1.5;
|
||||
if (this._screens[i].pixelRatio > 2) { targetDevicePixelRatio = 2; }
|
||||
let scaledWidth = (width * this._screens[i].pixelRatio) * (1 / targetDevicePixelRatio);
|
||||
let scaleRatio = scaledWidth / width;
|
||||
width = width * scaleRatio;
|
||||
height = height * scaleRatio;
|
||||
scale = 1 / scaleRatio;
|
||||
Log.Info('Small device with hDPI screen detected, auto scaling at ' + scaleRatio + ' to ' + width + 'x' + height);
|
||||
}
|
||||
|
||||
let clientServerRatioH = this._screens[i].containerHeight / height;
|
||||
let clientServerRatioW = this._screens[i].containerWidth / width;
|
||||
|
||||
this._screens[i].height = Math.floor(height * clientServerRatioH);
|
||||
this._screens[i].width = Math.floor(width * clientServerRatioW);
|
||||
this._screens[i].serverWidth = width;
|
||||
this._screens[i].serverHeight = height;
|
||||
this._screens[i].scale = this._screens[i].width / this._screens[i].serverWidth;
|
||||
|
||||
|
||||
for (i = 0; i < this._screens.length; i++) {
|
||||
this._screens[i].x2 = this._screens[i].x + this._screens[i].serverWidth;
|
||||
this._screens[i].y2 = this._screens[i].y + this._screens[i].serverHeight;
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._screens.length; i++) {
|
||||
data.serverWidth = Math.max(data.serverWidth, this._screens[i].x + this._screens[i].serverWidth);
|
||||
data.serverHeight = Math.max(data.serverHeight, this._screens[i].y + this._screens[i].serverHeight);
|
||||
}
|
||||
|
@ -302,21 +319,43 @@ export default class Display {
|
|||
return data;
|
||||
}
|
||||
|
||||
applyScreenPlan(screenPlan) {
|
||||
for (let i = 0; i < screenPlan.screens.length; i++) {
|
||||
for (let z = 0; z < this._screens.length; z++) {
|
||||
if (screenPlan.screens[i].screenID === this._screens[z].screenID) {
|
||||
this._screens[z].x = screenPlan.screens[i].x;
|
||||
this._screens[z].y = screenPlan.screens[i].y;
|
||||
}
|
||||
applyServerResolution(width, height, screenIndex) {
|
||||
for (let z = 0; z < this._screens.length; z++) {
|
||||
if (screenIndex === this._screens[z].screenIndex) {
|
||||
this._screens[z].serverReportedWidth = width;
|
||||
this._screens[z].serverReportedHeight = height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addScreen(screenID, width, height, pixelRatio, containerHeight, containerWidth) {
|
||||
applyScreenPlan(screenPlan) {
|
||||
let changes = false;
|
||||
for (let i = 0; i < screenPlan.screens.length; i++) {
|
||||
for (let z = 0; z < this._screens.length; z++) {
|
||||
if (screenPlan.screens[i].screenID === this._screens[z].screenID) {
|
||||
if (this._screens[z].x !== screenPlan.screens[i].x || this._screens[z].y !== screenPlan.screens[i].y) {
|
||||
this._screens[z].x = screenPlan.screens[i].x;
|
||||
this._screens[z].y = screenPlan.screens[i].y;
|
||||
changes = true;
|
||||
}
|
||||
if (this._screens[z].x2 !== this._screens[z].x + this._screens[z].serverWidth || this._screens[z].y2 !== this._screens[z].y + this._screens[z].serverHeight) {
|
||||
this._screens[z].x2 = this._screens[z].x + this._screens[z].serverWidth
|
||||
this._screens[z].y2 = this._screens[z].y + this._screens[z].serverHeight
|
||||
changes = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
||||
addScreen(screenID, width, height, pixelRatio, containerHeight, containerWidth, scale, serverWidth, serverHeight) {
|
||||
if (!this._isPrimaryDisplay) {
|
||||
throw new Error("Cannot add a screen to a secondary display.");
|
||||
}
|
||||
else if (containerHeight === 0 || containerWidth === 0 || pixelRatio === 0) {
|
||||
Log.Warn("Invalid screen configuration.");
|
||||
}
|
||||
let screenIdx = -1;
|
||||
|
||||
//Does the screen already exist?
|
||||
|
@ -329,12 +368,19 @@ export default class Display {
|
|||
if (screenIdx > 0) {
|
||||
//existing screen, update
|
||||
const screen = this._screens[screenIdx];
|
||||
screen.width = width;
|
||||
screen.height = height;
|
||||
screen.containerHeight = containerHeight;
|
||||
screen.containerWidth = containerWidth;
|
||||
screen.pixelRatio = pixelRatio;
|
||||
|
||||
if (screen.serverHeight !== serverHeight || screen.serverWidth !== serverWidth || screen.width !== width || screen.height !== height || screen.containerHeight !== containerHeight || screen.containerWidth !== containerWidth || screen.scale !== screen.scale || screen.pixelRatio !== screen.pixelRatio) {
|
||||
screen.width = width;
|
||||
screen.height = height;
|
||||
screen.containerHeight = containerHeight;
|
||||
screen.containerWidth = containerWidth;
|
||||
screen.pixelRatio = pixelRatio;
|
||||
screen.scale = scale;
|
||||
screen.serverWidth = serverWidth;
|
||||
screen.serverHeight = serverHeight;
|
||||
screen.x2 = screen.x + screen.serverWidth;
|
||||
screen.y2 = screen.y + screen.serverHeight;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
//New Screen, add to far right until user repositions it
|
||||
let x = 0;
|
||||
|
@ -347,15 +393,19 @@ export default class Display {
|
|||
screenIndex: this.screens.length,
|
||||
width: width, //client
|
||||
height: height, //client
|
||||
serverWidth: 0, //calculated
|
||||
serverHeight: 0, //calculated
|
||||
serverWidth: serverWidth,
|
||||
serverHeight: serverHeight,
|
||||
serverReportedWidth: 0,
|
||||
serverReportedHeight: 0,
|
||||
x: x,
|
||||
y: 0,
|
||||
pixelRatio: pixelRatio,
|
||||
containerHeight: containerHeight,
|
||||
containerWidth: containerWidth,
|
||||
channel: null,
|
||||
scale: 0
|
||||
scale: scale,
|
||||
x2: x + serverWidth,
|
||||
y2: serverHeight
|
||||
}
|
||||
|
||||
new_screen.channel = new BroadcastChannel(`screen_${screenID}_channel`);
|
||||
|
@ -363,7 +413,11 @@ export default class Display {
|
|||
|
||||
this._screens.push(new_screen);
|
||||
new_screen.channel.postMessage({ eventType: "registered", screenIndex: new_screen.screenIndex });
|
||||
|
||||
return new_screen.screenIndex;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
removeScreen(screenID) {
|
||||
|
@ -790,7 +844,6 @@ export default class Display {
|
|||
}
|
||||
|
||||
autoscale(containerWidth, containerHeight, scaleRatio=0) {
|
||||
|
||||
if (containerWidth === 0 || containerHeight === 0) {
|
||||
scaleRatio = 0;
|
||||
|
||||
|
@ -814,7 +867,9 @@ export default class Display {
|
|||
|
||||
_writeCtxBuffer() {
|
||||
//TODO: KASM-5450 Damage tracking with transparent rect overlay support
|
||||
this._targetCtx.drawImage(this._backbuffer, 0, 0);
|
||||
if (this._backbuffer.width > 0) {
|
||||
this._targetCtx.drawImage(this._backbuffer, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
_handleSecondaryDisplayMessage(event) {
|
||||
|
@ -836,7 +891,9 @@ export default class Display {
|
|||
let imageBmpPromise = createImageBitmap(rect.arr);
|
||||
imageBmpPromise.then( function(img) {
|
||||
this._transparentOverlayImg = img;
|
||||
this.enableCanvasBuffer = true;
|
||||
if (!this.enableCanvasBuffer) {
|
||||
this._enableCanvasBuffer = true;
|
||||
}
|
||||
}.bind(this) );
|
||||
this._transparentOverlayRect = rect;
|
||||
break;
|
||||
|
|
258
core/rfb.js
258
core/rfb.js
|
@ -124,7 +124,6 @@ export default class RFB extends EventTargetMixin {
|
|||
this._enabledContinuousUpdates = false;
|
||||
this._supportsSetDesktopSize = false;
|
||||
this._connectionID = window.location.href.split('?')[0].match(/^(.+)(\/)/)[0];
|
||||
this._screenIndex = 0;
|
||||
this._screenFlags = 0;
|
||||
this._qemuExtKeyEventSupported = false;
|
||||
|
||||
|
@ -419,20 +418,13 @@ export default class RFB extends EventTargetMixin {
|
|||
get clipViewport() { return this._clipViewport; }
|
||||
set clipViewport(viewport) {
|
||||
this._clipViewport = viewport;
|
||||
this._updateClip();
|
||||
}
|
||||
|
||||
get scaleViewport() { return this._scaleViewport; }
|
||||
set scaleViewport(scale) {
|
||||
this._scaleViewport = scale;
|
||||
// Scaling trumps clipping, so we may need to adjust
|
||||
// clipping when enabling or disabling scaling
|
||||
if (scale && this._clipViewport) {
|
||||
this._updateClip();
|
||||
}
|
||||
this._updateScale();
|
||||
if (!scale && this._clipViewport) {
|
||||
this._updateClip();
|
||||
if (this._scaleViewport !== scale) {
|
||||
this._scaleViewport = scale;
|
||||
this._pendingApplyResolutionChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -440,8 +432,8 @@ export default class RFB extends EventTargetMixin {
|
|||
set resizeSession(resize) {
|
||||
this._resizeSession = resize;
|
||||
if (resize) {
|
||||
this._requestRemoteResize();
|
||||
this.scaleViewport = true;
|
||||
this._pendingApplyResolutionChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -671,9 +663,20 @@ export default class RFB extends EventTargetMixin {
|
|||
}
|
||||
|
||||
get forcedResolutionX() { return this._forcedResolutionX; }
|
||||
set forcedResolutionX(value) {this._forcedResolutionX = value;}
|
||||
set forcedResolutionX(value) {
|
||||
if (value !== this._forcedResolutionX) {
|
||||
this._forcedResolutionX = value;
|
||||
this._pendingApplyResolutionChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
get forcedResolutionY() { return this._forcedResolutionY; }
|
||||
set forcedResolutionY(value) {this._forcedResolutionY = value;}
|
||||
set forcedResolutionY(value) {
|
||||
if (value !== this._forcedResolutionY) {
|
||||
this._forcedResolutionY = value;
|
||||
this._pendingApplyResolutionChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
get qualityLevel() {
|
||||
return this._qualityLevel;
|
||||
|
@ -732,16 +735,20 @@ export default class RFB extends EventTargetMixin {
|
|||
set enableHiDpi(value) {
|
||||
if (value !== this._hiDpi) {
|
||||
this._hiDpi = value;
|
||||
this._requestRemoteResize();
|
||||
if (this._display.screens.length > 1) {
|
||||
//force secondary displays to re-register and thus apply new hdpi setting
|
||||
this._proxyRFBMessage('forceResize', [ value ]);
|
||||
}
|
||||
this._pendingApplyResolutionChange = true;
|
||||
this._display.applyServerResolution(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ===== PUBLIC METHODS =====
|
||||
|
||||
refreshSecondaryDisplays() {
|
||||
//send secondary displays new settings
|
||||
if (this._display.screens.length > 1) {
|
||||
this._proxyRFBMessage('applySettings', [ this._hiDpi, this._clipViewport, this._scaleViewport, this._resizeSession, this._videoQuality, this._forcedResolutionX, this._forcedResolutionY ]);
|
||||
}
|
||||
}
|
||||
|
||||
attachSecondaryDisplay(details) {
|
||||
this._updateConnectionState('connecting');
|
||||
const screen = this._registerSecondaryDisplay(false, details);
|
||||
|
@ -783,10 +790,23 @@ export default class RFB extends EventTargetMixin {
|
|||
throw new Error("Screen plan contained fewer screens then there are registered.")
|
||||
}
|
||||
|
||||
this._display.applyScreenPlan(screenPlan);
|
||||
const size = this._screenSize();
|
||||
RFB.messages.setDesktopSize(this._sock, size, this._screenFlags);
|
||||
this._updateContinuousUpdates();
|
||||
//apply screen plan on primary display
|
||||
let changes = this._display.applyScreenPlan(screenPlan);
|
||||
|
||||
if (changes) {
|
||||
//send updates to secondary screens
|
||||
for (let i = 0; i < screenPlan.screens.length; i++) {
|
||||
for (let z = 1; z < fullPlan.screens.length; z++) {
|
||||
if (screenPlan.screens[i].screenID == fullPlan.screens[z].screenID) {
|
||||
this._proxyRFBMessage('applyScreenPlan', [ fullPlan.screens[z].screenID, fullPlan.screens[z].screenIndex, screenPlan.screens[i].width, screenPlan.screens[i].height, screenPlan.screens[i].x, screenPlan.screens[i].y ]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._pendingApplyResolutionChange = true;
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -818,16 +838,33 @@ export default class RFB extends EventTargetMixin {
|
|||
This function must be called after changing any properties that effect rendering quality
|
||||
*/
|
||||
updateConnectionSettings() {
|
||||
if (this._rfbConnectionState === 'connected') {
|
||||
if (this._rfbConnectionState === 'connected' && this._isPrimaryDisplay) {
|
||||
|
||||
if (this._pendingApplyVideoRes) {
|
||||
if (this._isPrimaryDisplay){
|
||||
RFB.messages.setMaxVideoResolution(this._sock, this._maxVideoResolutionX, this._maxVideoResolutionY);
|
||||
}
|
||||
RFB.messages.setMaxVideoResolution(this._sock, this._maxVideoResolutionX, this._maxVideoResolutionY);
|
||||
}
|
||||
|
||||
if (this._pendingApplyResolutionChange) {
|
||||
this._requestRemoteResize();
|
||||
// Scaling trumps clipping, so we may need to adjust
|
||||
// clipping when enabling or disabling scaling
|
||||
if (this._scaleViewport && this._clipViewport) {
|
||||
this._updateClip();
|
||||
}
|
||||
this._updateScale();
|
||||
if (!this._scaleViewport && this._clipViewport) {
|
||||
this._updateClip();
|
||||
}
|
||||
|
||||
if (this._display.screens.length > 1) {
|
||||
this.refreshSecondaryDisplays();
|
||||
}
|
||||
|
||||
if (this._resizeSession || (this._forcedResolutionX && this._forcedResolutionY)) {
|
||||
this._screenSize();
|
||||
this.dispatchEvent(new CustomEvent("screenregistered", {}));
|
||||
clearTimeout(this._resizeTimeout);
|
||||
this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._pendingApplyEncodingChanges) {
|
||||
|
@ -837,6 +874,20 @@ export default class RFB extends EventTargetMixin {
|
|||
this._pendingApplyVideoRes = false;
|
||||
this._pendingApplyEncodingChanges = false;
|
||||
this._pendingApplyResolutionChange = false;
|
||||
} else if (!this._isPrimaryDisplay) {
|
||||
if (this._pendingApplyResolutionChange) {
|
||||
if (this._scaleViewport && this._clipViewport) {
|
||||
this._updateClip();
|
||||
}
|
||||
this._updateScale();
|
||||
if (!this._scaleViewport && this._clipViewport) {
|
||||
this._updateClip();
|
||||
}
|
||||
}
|
||||
|
||||
if (this._resizeSession || (this._forcedResolutionX && this._forcedResolutionY)) {
|
||||
this._requestRemoteResize();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -851,6 +902,7 @@ export default class RFB extends EventTargetMixin {
|
|||
} else {
|
||||
this._updateConnectionState('disconnecting');
|
||||
this._unregisterSecondaryDisplay();
|
||||
this._rfbConnectionState = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1475,10 +1527,13 @@ export default class RFB extends EventTargetMixin {
|
|||
// If the window resized then our screen element might have
|
||||
// as well. Update the viewport dimensions.
|
||||
window.requestAnimationFrame(() => {
|
||||
this._screenSize();
|
||||
this._updateClip();
|
||||
this._updateScale();
|
||||
});
|
||||
|
||||
this.dispatchEvent(new CustomEvent("screenregistered", { }));
|
||||
|
||||
if (this._resizeSession) {
|
||||
// Request changing the resolution of the remote display to
|
||||
// the size of the local browser viewport.
|
||||
|
@ -1531,10 +1586,18 @@ export default class RFB extends EventTargetMixin {
|
|||
this._resizeTimeout = null;
|
||||
|
||||
if (this._isPrimaryDisplay) {
|
||||
if (!this._resizeSession || this._viewOnly ||
|
||||
!this._supportsSetDesktopSize) {
|
||||
if (
|
||||
(this._viewOnly || !this._supportsSetDesktopSize) ||
|
||||
(!this._resizeSession && !this._forcedResolutionX && !this._forcedResolutionY)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
//zero out the server reported resolution
|
||||
for (let i=0; i < this._display.screens.length; i++) {
|
||||
this._display.applyServerResolution(0, 0, this._display.screens[i].screenIndex);
|
||||
}
|
||||
|
||||
const size = this._screenSize();
|
||||
RFB.messages.setDesktopSize(this._sock, size, this._screenFlags);
|
||||
|
||||
|
@ -1550,17 +1613,14 @@ export default class RFB extends EventTargetMixin {
|
|||
top: window.screenTop
|
||||
}
|
||||
}
|
||||
this._registerSecondaryDisplay(false, details);
|
||||
}
|
||||
|
||||
if (this._display.screens.length > 1) {
|
||||
this.dispatchEvent(new CustomEvent("screenregistered", {}));
|
||||
|
||||
this._registerSecondaryDisplay(this._display.screens[0], details);
|
||||
}
|
||||
}
|
||||
|
||||
// Gets the the size of the available screen
|
||||
_screenSize (limited) {
|
||||
return this._display.getScreenSize(this.videoQuality, this.forcedResolutionX, this.forcedResolutionY, this._hiDpi, limited);
|
||||
return this._display.getScreenSize(this.videoQuality, this.forcedResolutionX, this.forcedResolutionY, this._hiDpi, limited, !this._resizeSession);
|
||||
}
|
||||
|
||||
_fixScrollbars() {
|
||||
|
@ -1740,22 +1800,22 @@ export default class RFB extends EventTargetMixin {
|
|||
...event.data.details,
|
||||
screenID: event.data.screenID
|
||||
}
|
||||
this._display.addScreen(event.data.screenID, event.data.width, event.data.height, event.data.pixelRatio, event.data.containerHeight, event.data.containerWidth);
|
||||
size = this._screenSize();
|
||||
RFB.messages.setDesktopSize(this._sock, size, this._screenFlags);
|
||||
this._sendEncodings();
|
||||
this._updateContinuousUpdates();
|
||||
let screenIndex = this._display.addScreen(event.data.screenID, event.data.width, event.data.height, event.data.pixelRatio, event.data.containerHeight, event.data.containerWidth, event.data.scale, event.data.serverWidth, event.data.serverHeight);
|
||||
this._proxyRFBMessage('screenRegistrationConfirmed', [ this._display.screens[screenIndex].screenID, screenIndex ]);
|
||||
clearTimeout(this._resizeTimeout);
|
||||
this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500);
|
||||
this.dispatchEvent(new CustomEvent("screenregistered", { detail: details }));
|
||||
Log.Info(`Secondary monitor (${event.data.screenID}) has been registered.`);
|
||||
break;
|
||||
case 'reattach':
|
||||
this._display.addScreen(event.data.screenID, event.data.width, event.data.height, event.data.pixelRatio, event.data.containerHeight, event.data.containerWidth);
|
||||
size = this._screenSize();
|
||||
RFB.messages.setDesktopSize(this._sock, size, this._screenFlags);
|
||||
this._sendEncodings();
|
||||
this._updateContinuousUpdates();
|
||||
this.dispatchEvent(new CustomEvent("screenregistered", {}));
|
||||
Log.Info(`Secondary monitor (${event.data.screenID}) has been reattached.`);
|
||||
let changes = this._display.addScreen(event.data.screenID, event.data.width, event.data.height, event.data.pixelRatio, event.data.containerHeight, event.data.containerWidth, event.data.scale, event.data.serverWidth, event.data.serverHeight);
|
||||
|
||||
if (changes) {
|
||||
clearTimeout(this._resizeTimeout);
|
||||
this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500);
|
||||
this.dispatchEvent(new CustomEvent("screenregistered", {}));
|
||||
Log.Info(`Secondary monitor (${event.data.screenID}) has been reattached.`);
|
||||
}
|
||||
break;
|
||||
case 'unregister':
|
||||
if (this._display.removeScreen(event.data.screenID)) {
|
||||
|
@ -1799,9 +1859,14 @@ export default class RFB extends EventTargetMixin {
|
|||
coords = this._display.getServerRelativeCoordinates(event.data.screenIndex, event.data.args[0], event.data.args[1]);
|
||||
this._mouseLastScreenIndex = event.data.screenIndex;
|
||||
this._mousePos = { 'x': coords[0], 'y': coords[1] };
|
||||
this._mouseButtonMask &= event.data.args[2];
|
||||
this._mouseButtonMask &= ~event.data.args[2];
|
||||
RFB.messages.pointerEvent(this._sock, this._mousePos.x, this._mousePos.y, this._mouseButtonMask);
|
||||
break;
|
||||
case 'scroll':
|
||||
coords = this._display.getServerRelativeCoordinates(event.data.screenIndex, event.data.args[0], event.data.args[1]);
|
||||
this._mouseLastScreenIndex = event.data.screenIndex;
|
||||
this._mousePos = { 'x': coords[0], 'y': coords[1] };
|
||||
RFB.messages.pointerEvent(this._sock, this._mousePos.x, this._mousePos.y, 0, event.data.args[2], event.data.args[3]);
|
||||
case 'keyEvent':
|
||||
RFB.messages.keyEvent(this._sock, ...event.data.args);
|
||||
break;
|
||||
|
@ -1831,10 +1896,38 @@ export default class RFB extends EventTargetMixin {
|
|||
this.disconnect();
|
||||
window.close();
|
||||
break;
|
||||
case 'forceResize':
|
||||
this._hiDpi = event.data.args[0];
|
||||
this._updateScale();
|
||||
this._requestRemoteResize();
|
||||
case 'applySettings':
|
||||
if (!this._isPrimaryDisplay) {
|
||||
this.enableHiDpi = event.data.args[0];
|
||||
this.clipViewport = event.data.args[1];
|
||||
this.scaleViewport = event.data.args[2];
|
||||
this.resizeSession = event.data.args[3];
|
||||
this.videoQuality = event.data.args[4];
|
||||
//TODO: add support for forced static resolution for multiple monitors
|
||||
//this._forcedResolutionX = event.data.args[5];
|
||||
//this._forcedResolutionY = event.data.args[6];
|
||||
|
||||
//TODO, do we need to do this twice
|
||||
this.scaleViewport = event.data.args[3];
|
||||
this.updateConnectionSettings();
|
||||
}
|
||||
|
||||
break;
|
||||
case 'applyScreenPlan':
|
||||
if (event.data.args[0] == this._display.screenID) {
|
||||
this._display.screens[0].screenIndex = event.data.args[1];
|
||||
this._display.screens[0].width = event.data.args[2];
|
||||
this._display.screens[0].height = event.data.args[3];
|
||||
this._display.screens[0].x = event.data.args[4];
|
||||
this._display.screens[0].y = event.data.args[5];
|
||||
|
||||
this.updateConnectionSettings();
|
||||
}
|
||||
break;
|
||||
case 'screenRegistrationConfirmed':
|
||||
if (event.data.args[0] == this._display.screenID) {
|
||||
this._display.screens[0].screenIndex = event.data.args[1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1854,23 +1947,25 @@ export default class RFB extends EventTargetMixin {
|
|||
|
||||
_registerSecondaryDisplay(currentScreen = false, details = null) {
|
||||
if (!this._isPrimaryDisplay) {
|
||||
//let screen = this._screenSize().screens[0];
|
||||
//
|
||||
const registerType = (currentScreen) ? 'reattach' : 'register'
|
||||
|
||||
let size = this._screenSize();
|
||||
this._display.resize(size.screens[0].serverWidth, size.screens[0].serverHeight);
|
||||
this._display.autoscale(size.screens[0].serverWidth, size.screens[0].serverHeight, size.screens[0].scale);
|
||||
screen = this._screenSize().screens[0];
|
||||
|
||||
const registertype = (currentScreen) ? 'reattach' : 'register'
|
||||
|
||||
let screen = size.screens[0];
|
||||
|
||||
let message = {
|
||||
eventType: registertype,
|
||||
eventType: registerType,
|
||||
screenID: screen.screenID,
|
||||
width: screen.width,
|
||||
height: screen.height,
|
||||
x: currentScreen.x || 0,
|
||||
y: currentScreen.y || 0,
|
||||
pixelRatio: screen.pixelRatio,
|
||||
scale: screen.scale,
|
||||
serverWidth: screen.serverWidth,
|
||||
serverHeight: screen.serverHeight,
|
||||
containerWidth: screen.containerWidth,
|
||||
containerHeight: screen.containerHeight,
|
||||
channel: null,
|
||||
|
@ -1941,7 +2036,13 @@ export default class RFB extends EventTargetMixin {
|
|||
//Ensure the window was not moved to a different screen with a different pixel ratio
|
||||
if (this._display.screens[0].pixelRatio !== window.devicePixelRatio) {
|
||||
Log.Debug("Window moved to another screen with different pixel ratio, sending resize request.");
|
||||
this._requestRemoteResize();
|
||||
if (this._isPrimaryDisplay && this._display.screens.length > 1) {
|
||||
//this.refreshSecondaryDisplays();
|
||||
this.dispatchEvent(new CustomEvent("screenregistered", {}));
|
||||
} else {
|
||||
this._requestRemoteResize();
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
Log.Debug("Mouse left Window");
|
||||
|
@ -2187,6 +2288,7 @@ export default class RFB extends EventTargetMixin {
|
|||
_sendMouse(x, y, mask) {
|
||||
if (this._rfbConnectionState !== 'connected') { return; }
|
||||
if (this._viewOnly) { return; } // View only, skip mouse events
|
||||
if (!this._isPrimaryDisplay) { return; }
|
||||
|
||||
if (this._pointerLock && this._pointerRelativeEnabled) {
|
||||
|
||||
|
@ -2194,22 +2296,13 @@ export default class RFB extends EventTargetMixin {
|
|||
var rel_16_x = toSignedRelative16bit(x - this._pointerLockPos.x);
|
||||
var rel_16_y = toSignedRelative16bit(y - this._pointerLockPos.y);
|
||||
|
||||
if (this._isPrimaryDisplay){
|
||||
RFB.messages.pointerEvent(this._sock, rel_16_x, rel_16_y, mask);
|
||||
} else {
|
||||
this._proxyRFBMessage('pointerEvent', [ rel_16_x, rel_16_y, mask ]);
|
||||
}
|
||||
RFB.messages.pointerEvent(this._sock, rel_16_x, rel_16_y, mask);
|
||||
|
||||
// reset the cursor position to center
|
||||
this._mousePos = { x: this._pointerLockPos.x , y: this._pointerLockPos.y };
|
||||
this._cursor.move(this._pointerLockPos.x, this._pointerLockPos.y);
|
||||
} else {
|
||||
if (this._isPrimaryDisplay) {
|
||||
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), mask);
|
||||
} else {
|
||||
this._proxyRFBMessage('pointerEvent', [ this._display.absX(x), this._display.absY(y), mask ]);
|
||||
}
|
||||
|
||||
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), mask);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2218,12 +2311,11 @@ export default class RFB extends EventTargetMixin {
|
|||
if (this._rfbConnectionState !== 'connected') { return; }
|
||||
if (this._viewOnly) { return; } // View only, skip mouse events
|
||||
|
||||
if (this._isPrimaryDisplay){
|
||||
if (this._isPrimaryDisplay) {
|
||||
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), 0, dX, dY);
|
||||
} else {
|
||||
this._proxyRFBMessage('pointerEvent', [ this._display.absX(x), this._display.absY(y), 0, dX, dY ]);
|
||||
this._proxyRFBMessage('scroll', [ x, y, dX, dY ]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_handleWheel(ev) {
|
||||
|
@ -4000,16 +4092,20 @@ export default class RFB extends EventTargetMixin {
|
|||
|
||||
for (let i = 0; i < numberOfScreens; i += 1) {
|
||||
// Save the id and flags of the first screen
|
||||
if (i === 0) {
|
||||
this._screenIndex = this._sock.rQshiftBytes(4); // id
|
||||
this._sock.rQskipBytes(2); // x-position
|
||||
this._sock.rQskipBytes(2); // y-position
|
||||
this._sock.rQskipBytes(2); // width
|
||||
this._sock.rQskipBytes(2); // height
|
||||
let sI = this._sock.rQshift32(); // id
|
||||
let x = this._sock.rQshift16(); // width
|
||||
let y = this._sock.rQshift16(); // height
|
||||
let w = this._sock.rQshift16(); // width
|
||||
let h = this._sock.rQshift16(); // height
|
||||
if (i == 0) {
|
||||
this._screenIndex = 0;
|
||||
this._screenFlags = this._sock.rQshiftBytes(4); // flags
|
||||
} else {
|
||||
this._sock.rQskipBytes(16);
|
||||
this._sock.rQskipBytes(4);
|
||||
}
|
||||
|
||||
this._display.applyServerResolution(w, h, i);
|
||||
Log.Debug(`Server reported screen ${sI} with resolution ${w}x${h} at ${x}x${y}`);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
8
vnc.html
8
vnc.html
|
@ -426,6 +426,14 @@
|
|||
<label for="noVNC_setting_max_video_resolution_y">Video Mode Height:</label>
|
||||
<input id="noVNC_setting_max_video_resolution_y" type="number" min="100" max="2160" value="540">
|
||||
</li>
|
||||
<li class="noVNC_hidden">
|
||||
<label for="noVNC_setting_forced_resolution_x">Static Width:</label>
|
||||
<input id="noVNC_setting_forced_resolution_x" type="number" min="100" max="3840" value="960">
|
||||
</li>
|
||||
<li class="noVNC_hidden">
|
||||
<label for="noVNC_setting_forced_resolution_y">Static Height:</label>
|
||||
<input id="noVNC_setting_forced_resolution_y" type="number" min="100" max="2160" value="540">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
|
|
Loading…
Reference in New Issue