From 15afc5f42ddf98230451cbd379e94b517ec331c3 Mon Sep 17 00:00:00 2001 From: Alberto Larraz Date: Wed, 21 Jan 2026 06:19:19 +0000 Subject: [PATCH] feat(rfb): add encoding, FPS and bandwidth event emissions - Add encodingchange event when encoding type changes - Add FPS counter event emitted every second - Add bandwidth tracking in websock.js (bytes sent/received) - Add bandwidth event with download/upload kbps every 2 seconds --- core/rfb.js | 32 ++++++++++++++++++++++++++++++++ core/websock.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/core/rfb.js b/core/rfb.js index 1073a878b..acd1419b6 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -187,6 +187,12 @@ export default class RFB extends EventTargetMixin { encoding: null, }; + // Encoding and FPS tracking + this._lastEncoding = null; + this._frameCount = 0; + this._lastFpsTime = performance.now(); + this._lastBandwidthTime = performance.now(); + // Mouse state this._mousePos = {}; this._mouseButtonMask = 0; @@ -2663,6 +2669,13 @@ export default class RFB extends EventTargetMixin { this._FBU.encoding = this._sock.rQshift32(); /* Encodings are signed */ this._FBU.encoding >>= 0; + + // Emit encoding change event for real encodings (not pseudo-encodings) + if (this._lastEncoding !== this._FBU.encoding && this._FBU.encoding >= 0) { + this._lastEncoding = this._FBU.encoding; + this.dispatchEvent(new CustomEvent("encodingchange", + { detail: { encoding: this._FBU.encoding } })); + } } if (!this._handleRect()) { @@ -2675,6 +2688,25 @@ export default class RFB extends EventTargetMixin { this._display.flip(); + // FPS counter + this._frameCount++; + const now = performance.now(); + if (now - this._lastFpsTime >= 1000) { + this.dispatchEvent(new CustomEvent("fps", + { detail: { fps: this._frameCount } })); + this._frameCount = 0; + this._lastFpsTime = now; + } + + // Bandwidth counter (every 2 seconds for smoother readings) + if (now - this._lastBandwidthTime >= 2000) { + const stats = this._sock.getBandwidthStats(); + this.dispatchEvent(new CustomEvent("bandwidth", + { detail: stats })); + this._sock.resetBandwidthStats(); + this._lastBandwidthTime = now; + } + return true; // We finished this FBU } diff --git a/core/websock.js b/core/websock.js index ee8a4bc42..1f15e88d5 100644 --- a/core/websock.js +++ b/core/websock.js @@ -68,6 +68,34 @@ export default class Websock { close: () => {}, error: () => {} }; + + // Bandwidth tracking + this._bytesReceived = 0; + this._bytesSent = 0; + this._bandwidthStartTime = performance.now(); + } + + // Bandwidth statistics + getBandwidthStats() { + const now = performance.now(); + const elapsed = (now - this._bandwidthStartTime) / 1000; // seconds + if (elapsed < 0.1) return { down: 0, up: 0, totalDown: 0, totalUp: 0 }; + + const downKbps = (this._bytesReceived * 8 / 1000) / elapsed; + const upKbps = (this._bytesSent * 8 / 1000) / elapsed; + + return { + down: downKbps, + up: upKbps, + totalDown: this._bytesReceived, + totalUp: this._bytesSent + }; + } + + resetBandwidthStats() { + this._bytesReceived = 0; + this._bytesSent = 0; + this._bandwidthStartTime = performance.now(); } // Getters and setters @@ -220,6 +248,7 @@ export default class Websock { flush() { if (this._sQlen > 0 && this.readyState === 'open') { + this._bytesSent += this._sQlen; this._websocket.send(new Uint8Array(this._sQ.buffer, 0, this._sQlen)); this._sQlen = 0; } @@ -354,6 +383,7 @@ export default class Websock { this._rQi = 0; } const u8 = new Uint8Array(e.data); + this._bytesReceived += u8.length; if (u8.length > this._rQbufferSize - this._rQlen) { this._expandCompactRQ(u8.length); }