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:
Matt McClaskey 2024-01-26 11:09:56 -05:00 committed by GitHub
parent f6c1d8c668
commit 4825e12f62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 360 additions and 171 deletions

View File

@ -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();
}
},

View File

@ -142,6 +142,7 @@ const UI = {
}
}
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);
}
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();

View File

@ -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,36 +229,48 @@ 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 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
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
@ -263,8 +280,8 @@ export default class Display {
}
//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;
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
@ -287,12 +304,12 @@ export default class Display {
this._screens[i].width = Math.floor(width * clientServerRatioW);
this._screens[i].serverWidth = width;
this._screens[i].serverHeight = height;
this._screens[i].scale = scale;
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++) {
applyServerResolution(width, height, screenIndex) {
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;
}
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];
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,8 +867,10 @@ export default class Display {
_writeCtxBuffer() {
//TODO: KASM-5450 Damage tracking with transparent rect overlay support
if (this._backbuffer.width > 0) {
this._targetCtx.drawImage(this._backbuffer, 0, 0);
}
}
_handleSecondaryDisplayMessage(event) {
if (!this._isPrimaryDisplay && event.data) {
@ -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;

View File

@ -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) {
if (this._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();
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);
}
}
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();
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,8 +2036,14 @@ 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.");
if (this._isPrimaryDisplay && this._display.screens.length > 1) {
//this.refreshSecondaryDisplays();
this.dispatchEvent(new CustomEvent("screenregistered", {}));
} else {
this._requestRemoteResize();
}
}
} else {
Log.Debug("Mouse left Window");
Log.Debug(ev);
@ -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 ]);
}
// 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 ]);
}
}
}
@ -2221,9 +2314,8 @@ export default class RFB extends EventTargetMixin {
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}`);
}
/*

View File

@ -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>