Improve detection of Qt VNC servers to handle BGR color mode

This commit is contained in:
f-trycua 2025-04-27 14:01:52 -07:00
parent d7a37730e6
commit 0a67102838
2 changed files with 38 additions and 12 deletions

View File

@ -12,7 +12,7 @@ export default class RawDecoder {
this._lines = 0;
}
decodeRect(x, y, width, height, sock, display, depth) {
decodeRect(x, y, width, height, sock, display, depth, bgrMode = false) {
if ((width === 0) || (height === 0)) {
return true;
}
@ -43,11 +43,20 @@ export default class RawDecoder {
newdata[i * 4 + 3] = 255;
}
data = newdata;
}
// Max sure the image is fully opaque
for (let i = 0; i < width; i++) {
data[i * 4 + 3] = 255;
} else if (bgrMode) {
// In bgrMode we need to switch the red and blue bytes
// so that the data is in RGB order
for (let i = 0; i < width; i++) {
let j = i * 4;
let red = data[j];
data[j] = data[j + 2];
data[j + 2] = red;
}
} else {
// Make sure the image is fully opaque
for (let i = 0; i < width; i++) {
data[i * 4 + 3] = 255;
}
}
display.blitImage(x, curY, width, 1, data, 0);

View File

@ -2210,18 +2210,29 @@ export default class RFB extends EventTargetMixin {
// we're past the point where we could backtrack, so it's safe to call this
this._setDesktopName(name);
Log.Info("Connected to server: '" + this._fbName + "'");
this._resize(width, height);
if (!this._viewOnly) { this._keyboard.grab(); }
this._fbDepth = 24;
this._BGRmode = false;
if (this._fbName === "Intel(r) AMT KVM") {
Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Using low color mode.");
this._fbDepth = 8;
} else if (this._fbName === "Qt for Embedded Linux VNC Server" ||
this._fbName.indexOf("Qt") !== -1) {
// Qt has a bug (as of QT 6.4) where, if the endian-ness is 'little' and the _fbDepth is 24, it does not honour the noVNC reqested
// redshift/blueshift values. In these conditions it triggers a performance bypass in the Qt code which just uses the raw
// Qt frambuffer byte order (https://github.com/qt/qtbase/blob/5.15.2/src/plugins/platforms/vnc/qvncclient.cpp#L113) which is BGR
// vs the RGB noVNC wants. Since noVNC always operates in little endian and fbDepth 24, it always triggers this bug
// So, for Qt we change to BGR ordering. Note that Qt only uses raw encoding and noVNC only supports this mode in the raw decoder
Log.Info("Detected Qt VNC server. Enabling BGR color mode.");
this._BGRmode = true;
}
RFB.messages.pixelFormat(this._sock, this._fbDepth, true);
RFB.messages.pixelFormat(this._sock, this._fbDepth, true, this._BGRmode);
this._sendEncodings();
RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fbWidth, this._fbHeight);
@ -2989,7 +3000,7 @@ export default class RFB extends EventTargetMixin {
return decoder.decodeRect(this._FBU.x, this._FBU.y,
this._FBU.width, this._FBU.height,
this._sock, this._display,
this._fbDepth);
this._fbDepth, this._BGRmode);
} catch (err) {
this._fail("Error decoding rect: " + err);
return false;
@ -3314,7 +3325,7 @@ RFB.messages = {
sock.flush();
},
pixelFormat(sock, depth, trueColor) {
pixelFormat(sock, depth, trueColor, bgrMode) {
let bpp;
if (depth > 16) {
@ -3342,9 +3353,15 @@ RFB.messages = {
sock.sQpush16((1 << bits) - 1); // green-max
sock.sQpush16((1 << bits) - 1); // blue-max
sock.sQpush8(bits * 0); // red-shift
sock.sQpush8(bits * 1); // green-shift
sock.sQpush8(bits * 2); // blue-shift
if (bgrMode) {
sock.sQpush8(bits * 2); // red-shift
sock.sQpush8(bits * 1); // green-shift
sock.sQpush8(bits * 0); // blue-shift
} else {
sock.sQpush8(bits * 0); // red-shift
sock.sQpush8(bits * 1); // green-shift
sock.sQpush8(bits * 2); // blue-shift
}
sock.sQpush8(0); // padding
sock.sQpush8(0); // padding