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; 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)) { if ((width === 0) || (height === 0)) {
return true; return true;
} }
@ -43,11 +43,20 @@ export default class RawDecoder {
newdata[i * 4 + 3] = 255; newdata[i * 4 + 3] = 255;
} }
data = newdata; data = newdata;
} } else if (bgrMode) {
// In bgrMode we need to switch the red and blue bytes
// Max sure the image is fully opaque // so that the data is in RGB order
for (let i = 0; i < width; i++) { for (let i = 0; i < width; i++) {
data[i * 4 + 3] = 255; 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); 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 // we're past the point where we could backtrack, so it's safe to call this
this._setDesktopName(name); this._setDesktopName(name);
Log.Info("Connected to server: '" + this._fbName + "'");
this._resize(width, height); this._resize(width, height);
if (!this._viewOnly) { this._keyboard.grab(); } if (!this._viewOnly) { this._keyboard.grab(); }
this._fbDepth = 24; this._fbDepth = 24;
this._BGRmode = false;
if (this._fbName === "Intel(r) AMT KVM") { if (this._fbName === "Intel(r) AMT KVM") {
Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Using low color mode."); Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Using low color mode.");
this._fbDepth = 8; 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(); this._sendEncodings();
RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fbWidth, this._fbHeight); 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, return decoder.decodeRect(this._FBU.x, this._FBU.y,
this._FBU.width, this._FBU.height, this._FBU.width, this._FBU.height,
this._sock, this._display, this._sock, this._display,
this._fbDepth); this._fbDepth, this._BGRmode);
} catch (err) { } catch (err) {
this._fail("Error decoding rect: " + err); this._fail("Error decoding rect: " + err);
return false; return false;
@ -3314,7 +3325,7 @@ RFB.messages = {
sock.flush(); sock.flush();
}, },
pixelFormat(sock, depth, trueColor) { pixelFormat(sock, depth, trueColor, bgrMode) {
let bpp; let bpp;
if (depth > 16) { if (depth > 16) {
@ -3342,9 +3353,15 @@ RFB.messages = {
sock.sQpush16((1 << bits) - 1); // green-max sock.sQpush16((1 << bits) - 1); // green-max
sock.sQpush16((1 << bits) - 1); // blue-max sock.sQpush16((1 << bits) - 1); // blue-max
sock.sQpush8(bits * 0); // red-shift if (bgrMode) {
sock.sQpush8(bits * 1); // green-shift sock.sQpush8(bits * 2); // red-shift
sock.sQpush8(bits * 2); // blue-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
sock.sQpush8(0); // padding sock.sQpush8(0); // padding