Implement UI for controlling ATEN-specific settings
Add video settings change callback to Ast2100Decoder for UI integration. Implement slider widget (used to select AST2100 quantization tables).
This commit is contained in:
parent
0d048acaca
commit
eb386b7672
|
|
@ -147,6 +147,11 @@ input[type=button]:active, select:active {
|
|||
background: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250));
|
||||
}
|
||||
|
||||
/* Settings slider: video quality (quantization table selector) in AST2100 mode. */
|
||||
#noVNC_settings #noVNC_setting_ast2100_quality {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
/* ----------------------------------------
|
||||
* WebKit centering hacks
|
||||
* ----------------------------------------
|
||||
|
|
|
|||
105
app/ui.js
105
app/ui.js
|
|
@ -100,6 +100,12 @@ var UI;
|
|||
lastKeyboardinput: null,
|
||||
defaultKeyboardinputLen: 100,
|
||||
|
||||
// True if we are connected to an ATEN iKVM server speaking the AST2100 video encoding.
|
||||
// This variable tracks whether the extra UI elements used to configure video settings
|
||||
// for the AST2100 encoding have been shown yet; it's set on the first FramebufferUpdate
|
||||
// message that we receive.
|
||||
_ast2100_videoSettingsInitialized: false,
|
||||
|
||||
// Setup rfb object, load settings from browser storage, then call
|
||||
// UI.init to setup the UI/menus
|
||||
load: function(callback) {
|
||||
|
|
@ -392,7 +398,9 @@ var UI;
|
|||
'onBell': UI.bell,
|
||||
'onFBUComplete': UI.initialResize,
|
||||
'onFBResize': UI.updateSessionSize,
|
||||
'onDesktopName': UI.updateDesktopName});
|
||||
'onDesktopName': UI.updateDesktopName,
|
||||
'ast2100_onVideoSettingsChanged': UI.ast2100_handleVideoSettingsChanged
|
||||
});
|
||||
return true;
|
||||
} catch (exc) {
|
||||
var msg = "Unable to create RFB client -- " + exc;
|
||||
|
|
@ -436,7 +444,9 @@ var UI;
|
|||
document.documentElement.classList.add("noVNC_disconnecting");
|
||||
break;
|
||||
case 'disconnected':
|
||||
UI.connected = false;
|
||||
UI.showStatus(_("Disconnected"));
|
||||
UI.ast2100_reset();
|
||||
break;
|
||||
default:
|
||||
msg = "Invalid UI state";
|
||||
|
|
@ -844,6 +854,15 @@ var UI;
|
|||
UI.saveSetting('repeaterID');
|
||||
UI.saveSetting('logging');
|
||||
|
||||
if (UI._ast2100_videoSettingsInitialized) {
|
||||
var videoSettings = UI.ast2100_getConfiguredSettings();
|
||||
if (videoSettings != UI._ast2100_serverVideoSettings)
|
||||
UI.rfb.atenChangeVideoSettings(
|
||||
videoSettings.quantTableSelectorLuma,
|
||||
videoSettings.quantTableSelectorChroma,
|
||||
videoSettings.subsamplingMode);
|
||||
}
|
||||
|
||||
// Settings with immediate (non-connected related) effect
|
||||
WebUtil.init_logging(UI.getSetting('logging'));
|
||||
UI.updateViewClip();
|
||||
|
|
@ -963,6 +982,90 @@ var UI;
|
|||
/* ------^-------
|
||||
* /XVP
|
||||
* ==============
|
||||
* AST2100
|
||||
* ------v------*/
|
||||
|
||||
ast2100_handleVideoSettingsChanged: function (settings) {
|
||||
if (!UI._ast2100_videoSettingsInitialized)
|
||||
UI.ast2100_setDefaultSettings(settings);
|
||||
UI.ast2100_updateVideoSettings(settings);
|
||||
},
|
||||
|
||||
// Called the first time we receive a FramebufferUpdate object. Responsible
|
||||
// for telling the server about any configured default settings.
|
||||
ast2100_setDefaultSettings: function (settings) {
|
||||
// Convert the settings that noVNC has been configured to set for all
|
||||
// AST2100 servers to the familiar videoSettings format.
|
||||
var defaultQuality = parseInt(UI.ast2100_quality);
|
||||
var defaultSettings = {
|
||||
quantTableSelectorLuma: defaultQuality,
|
||||
quantTableSelectorChroma: defaultQuality,
|
||||
subsamplingMode: parseInt(UI.ast2100_subsamplingMode)
|
||||
};
|
||||
|
||||
// If defaults were not given or were invalid, stick with what the
|
||||
// server is already using.
|
||||
if (!(defaultSettings.subsamplingMode == 422 || defaultSettings.subsamplingMode == 444))
|
||||
defaultSettings.subsamplingMode = settings.subsamplingMode;
|
||||
if (!inRangeIncl(defaultQuality, 0x0, 0xB)) {
|
||||
defaultSettings.quantTableSelectorLuma = settings.quantTableSelectorLuma;
|
||||
defaultSettings.quantTableSelectorChroma = settings.quantTableSelectorChroma;
|
||||
}
|
||||
|
||||
if (defaultSettings != settings)
|
||||
UI.rfb.atenChangeVideoSettings(
|
||||
defaultSettings.quantTableSelectorLuma,
|
||||
defaultSettings.quantTableSelectorChroma,
|
||||
defaultSettings.subsamplingMode);
|
||||
},
|
||||
|
||||
// Should be called at init time and after disconnects. This is sort
|
||||
// of the opposite of _init().
|
||||
ast2100_reset: function() {
|
||||
document.getElementById("noVNC_ast2100_settings").classList.add('noVNC_hidden');
|
||||
UI._ast2100_videoSettingsInitialized = false;
|
||||
UI._ast2100_serverVideoSettings = undefined;
|
||||
},
|
||||
|
||||
// Updates the UI to reflect values received from the server.
|
||||
ast2100_updateVideoSettings: function (videoSettings) {
|
||||
Util.Info("AST2100 video settings changed:");
|
||||
Util.Info(videoSettings);
|
||||
|
||||
// We use this to tell if the user changed anything when they apply
|
||||
// settings.
|
||||
UI._ast2100_serverVideoSettings = videoSettings;
|
||||
|
||||
// First run: tell UI to show video quality controls, now that we
|
||||
// know we are on a machine that supports them, and we know their
|
||||
// current values.
|
||||
if (!UI._ast2100_videoSettingsInitialized) {
|
||||
document.getElementById("noVNC_ast2100_settings").classList.remove('noVNC_hidden');
|
||||
UI._ast2100_videoSettingsInitialized = true;
|
||||
}
|
||||
|
||||
// Average the two quant table selectors as a poor way of dealing
|
||||
// with the fact that they can, technically, be different.
|
||||
var quality = ~~((videoSettings.quantTableSelectorLuma + videoSettings.quantTableSelectorChroma) / 2);
|
||||
document.getElementById("noVNC_setting_ast2100_quality").value = quality;
|
||||
|
||||
// Either 444 or 422 (which is really 4:2:0).
|
||||
document.getElementById("noVNC_setting_ast2100_subsampling").value = videoSettings.subsamplingMode;
|
||||
},
|
||||
|
||||
// Returns the current state of the UI.
|
||||
ast2100_getConfiguredSettings: function () {
|
||||
var quality = +document.getElementById("noVNC_setting_ast2100_quality").value;
|
||||
return {
|
||||
quantTableSelectorLuma: quality,
|
||||
quantTableSelectorChroma: quality,
|
||||
subsamplingMode: +document.getElementById("noVNC_setting_ast2100_subsampling").value
|
||||
};
|
||||
},
|
||||
|
||||
/* ------^-------
|
||||
* /AST2100
|
||||
* ==============
|
||||
* CLIPBOARD
|
||||
* ------v------*/
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ var Ast2100Decoder;
|
|||
Ast2100Decoder = function (defaults) {
|
||||
|
||||
this._blitCallback = defaults.blitCallback;
|
||||
this._videoSettingsChangedCallback = defaults.videoSettingsChangedCallback;
|
||||
this._frame_width = defaults.width;
|
||||
this._frame_height = defaults.height;
|
||||
|
||||
|
|
@ -158,6 +159,18 @@ var Ast2100Decoder;
|
|||
this._frame_height = height;
|
||||
},
|
||||
|
||||
// Each quant table selector is between 0x0 (lowest quality) and 0xB (highest quality). The ATEN client shows a
|
||||
// single quality slider, which changes both values in tandem. The server sends all three values with each
|
||||
// FramebufferUpdate message, so these values are updated with every call to decode(). They will be -1 before
|
||||
// the first frame is decoded.
|
||||
getVideoSettings: function () {
|
||||
return {
|
||||
quantTableSelectorLuma: this._loadedQuantTables[0],
|
||||
quantTableSelectorChroma: this._loadedQuantTables[1],
|
||||
subsamplingMode: this.subsamplingMode
|
||||
};
|
||||
},
|
||||
|
||||
decode: function (data) {
|
||||
|
||||
var mcuIdx = 0;
|
||||
|
|
@ -174,10 +187,13 @@ var Ast2100Decoder;
|
|||
var quantTableSelectorLuma = data[0]; // 0 <= x <= 0xB
|
||||
var quantTableSelectorChroma = data[1]; // 0 <= x <= 0xB
|
||||
var subsamplingMode = (data[2] << 8) | data[3]; // 422u or 444u
|
||||
|
||||
var changedSettings = false;
|
||||
if (this.subsamplingMode != subsamplingMode) {
|
||||
if (verboseVideoSettings)
|
||||
console.log('decode(): new subsampling mode: '+subsamplingMode);
|
||||
this.subsamplingMode = subsamplingMode;
|
||||
changedSettings = true;
|
||||
}
|
||||
|
||||
// The remainder of the stream is byte-swapped in four-byte chunks.
|
||||
|
|
@ -193,6 +209,7 @@ var Ast2100Decoder;
|
|||
console.log('decode(): loading new luma quant table: '+fmt_u8(quantTableSelectorLuma));
|
||||
this._loadQuantTable(0, ATEN_QT_LUMA[quantTableSelectorLuma]);
|
||||
this._loadedQuantTables[0] = quantTableSelectorLuma;
|
||||
changedSettings = true;
|
||||
}
|
||||
if (quantTableSelectorChroma != this._loadedQuantTables[1]) {
|
||||
if (!inRangeIncl(quantTableSelectorChroma, 0, 0xB))
|
||||
|
|
@ -201,10 +218,15 @@ var Ast2100Decoder;
|
|||
console.log('decode(): loading new chroma quant table: '+fmt_u8(quantTableSelectorChroma));
|
||||
this._loadQuantTable(1, ATEN_QT_CHROMA[quantTableSelectorChroma]);
|
||||
this._loadedQuantTables[1] = quantTableSelectorChroma;
|
||||
changedSettings = true;
|
||||
}
|
||||
|
||||
if (this.subsamplingMode != 422 && this.subsamplingMode != 444)
|
||||
throw 'Unexpected value for subsamplingMode: 0x' + fmt_u16(this.subsamplingMode);
|
||||
|
||||
if (changedSettings && this._videoSettingsChangedCallback)
|
||||
this._videoSettingsChangedCallback(this.getVideoSettings());
|
||||
|
||||
// The remainder of the stream is byte-swapped in four-byte chunks. BitStream takes care of this.
|
||||
this._stream = new BitStream({data: data});
|
||||
this._stream.skip(16);
|
||||
|
|
|
|||
78
core/rfb.js
78
core/rfb.js
|
|
@ -107,6 +107,8 @@
|
|||
subrects: 0, // RRE
|
||||
lines: 0, // RAW
|
||||
tiles: 0, // HEXTILE
|
||||
aten_len: -1, // ATEN
|
||||
aten_type: -1, // ATEN
|
||||
bytes: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
|
@ -170,6 +172,12 @@
|
|||
'wsProtocols': ['binary'], // Protocols to use in the WebSocket connection
|
||||
'repeaterID': '', // [UltraVNC] RepeaterID to connect to
|
||||
'viewportDrag': false, // Move the viewport on mouse drags
|
||||
'ast2100_quality': -1, // If set, use this quality upon connection to a server
|
||||
// using the AST2100 video encoding. Ranges from 0 (lowest)
|
||||
// to 0xB (highest) quality.
|
||||
'ast2100_subsamplingMode': -1, // If set, use this subsampling mode upon connection to a
|
||||
// server using the AST2100 video encoding. The value may
|
||||
// either be 444 or 422 (which is really 4:2:0 subsampling).
|
||||
|
||||
// Callback functions
|
||||
'onUpdateState': function () { }, // onUpdateState(rfb, state, oldstate): connection state change
|
||||
|
|
@ -182,7 +190,8 @@
|
|||
'onFBUComplete': function () { }, // onFBUComplete(rfb, fbu): RFB FBU received and processed
|
||||
'onFBResize': function () { }, // onFBResize(rfb, width, height): frame buffer resized
|
||||
'onDesktopName': function () { }, // onDesktopName(rfb, name): desktop name received
|
||||
'onXvpInit': function () { } // onXvpInit(version): XVP extensions active for this connection
|
||||
'onXvpInit': function () { }, // onXvpInit(version): XVP extensions active for this connection
|
||||
'ast2100_onVideoSettingsChanged': function () { }
|
||||
});
|
||||
|
||||
// main setup
|
||||
|
|
@ -312,12 +321,19 @@
|
|||
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
|
||||
Util.Info("Sending Ctrl-Alt-Del");
|
||||
|
||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Control_L, 1);
|
||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Alt_L, 1);
|
||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Delete, 1);
|
||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Delete, 0);
|
||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Alt_L, 0);
|
||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Control_L, 0);
|
||||
var keyEvent;
|
||||
if (this._rfb_atenikvm) {
|
||||
keyEvent = RFB.messages.atenKeyEvent;
|
||||
} else {
|
||||
keyEvent = RFB.messages.keyEvent;
|
||||
}
|
||||
|
||||
keyEvent(this._sock, KeyTable.XK_Control_L, 1);
|
||||
keyEvent(this._sock, KeyTable.XK_Alt_L, 1);
|
||||
keyEvent(this._sock, KeyTable.XK_Delete, 1);
|
||||
keyEvent(this._sock, KeyTable.XK_Delete, 0);
|
||||
keyEvent(this._sock, KeyTable.XK_Alt_L, 0);
|
||||
keyEvent(this._sock, KeyTable.XK_Control_L, 0);
|
||||
return true;
|
||||
},
|
||||
|
||||
|
|
@ -344,13 +360,21 @@
|
|||
// followed by an up key.
|
||||
sendKey: function (keysym, down) {
|
||||
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
|
||||
|
||||
var keyEvent;
|
||||
if (this._rfb_atenikvm) {
|
||||
keyEvent = RFB.messages.atenKeyEvent;
|
||||
} else {
|
||||
keyEvent = RFB.messages.keyEvent;
|
||||
}
|
||||
|
||||
if (typeof down !== 'undefined') {
|
||||
Util.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
|
||||
RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
|
||||
keyEvent(this._sock, keysym, down ? 1 : 0);
|
||||
} else {
|
||||
Util.Info("Sending keysym (down + up): " + keysym);
|
||||
RFB.messages.keyEvent(this._sock, keysym, 1);
|
||||
RFB.messages.keyEvent(this._sock, keysym, 0);
|
||||
keyEvent(this._sock, keysym, 1);
|
||||
keyEvent(this._sock, keysym, 0);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
|
@ -685,8 +709,12 @@
|
|||
}
|
||||
} else {
|
||||
keysym = keyevent.keysym.keysym;
|
||||
if (this._rfb_atenikvm) {
|
||||
RFB.messages.atenKeyEvent(this._sock, keysym, down);
|
||||
} else {
|
||||
RFB.messages.keyEvent(this._sock, keysym, down);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_handleMouseButton: function (x, y, down, bmask) {
|
||||
|
|
@ -709,8 +737,12 @@
|
|||
// If the viewport didn't actually move, then treat as a mouse click event
|
||||
// Send the button down event here, as the button up event is sent at the end of this function
|
||||
if (!this._viewportHasMoved && !this._view_only) {
|
||||
if (this._rfb_atenikvm) {
|
||||
RFB.messages.atenPointerEvent(this._sock, this._display.absX(x), this._display.absY(y), bmask);
|
||||
} else {
|
||||
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), bmask);
|
||||
}
|
||||
}
|
||||
this._viewportHasMoved = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -718,7 +750,11 @@
|
|||
if (this._view_only) { return; } // View only, skip mouse events
|
||||
|
||||
if (this._rfb_connection_state !== 'connected') { return; }
|
||||
if (this._rfb_atenikvm) {
|
||||
RFB.messages.atenPointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
|
||||
} else {
|
||||
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
|
||||
}
|
||||
},
|
||||
|
||||
_handleMouseMove: function (x, y) {
|
||||
|
|
@ -745,7 +781,11 @@
|
|||
if (this._view_only) { return; } // View only, skip mouse events
|
||||
|
||||
if (this._rfb_connection_state !== 'connected') { return; }
|
||||
if (this._rfb_atenikvm) {
|
||||
RFB.messages.atenPointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
|
||||
} else {
|
||||
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
|
||||
}
|
||||
},
|
||||
|
||||
// Message Handlers
|
||||
|
|
@ -1233,13 +1273,6 @@
|
|||
this._pixelFormat.red_shift = 10;
|
||||
this._pixelFormat.green_shift = 5;
|
||||
this._pixelFormat.blue_shift = 0;
|
||||
|
||||
// XXX(kelleyk): Doing this will break interaction with non-ATEN
|
||||
// servers until the page is reloaded; it also breaks the mouse/
|
||||
// keyboard portions of the test suite!
|
||||
//
|
||||
RFB.messages.keyEvent = RFB.messages.atenKeyEvent;
|
||||
RFB.messages.pointerEvent = RFB.messages.atenPointerEvent;
|
||||
}
|
||||
|
||||
if (this._convert_color)
|
||||
|
|
@ -1771,6 +1804,9 @@
|
|||
['wsProtocols', 'rw', 'arr'], // Protocols to use in the WebSocket connection
|
||||
['repeaterID', 'rw', 'str'], // [UltraVNC] RepeaterID to connect to
|
||||
['viewportDrag', 'rw', 'bool'], // Move the viewport on mouse drags
|
||||
['ast2100_quality', 'rw','int'], // Ranges from 0 (lowest) to 0xB (highest) quality.
|
||||
['ast2100_subsamplingMode', 'rw', 'int'], // Chroma subsampling; either 444 or 422 (which is
|
||||
// really 4:2:0 subsampling).
|
||||
|
||||
// Callback functions
|
||||
['onUpdateState', 'rw', 'func'], // onUpdateState(rfb, state, oldstate): connection state change
|
||||
|
|
@ -1783,7 +1819,9 @@
|
|||
['onFBUComplete', 'rw', 'func'], // onFBUComplete(rfb, fbu): RFB FBU received and processed
|
||||
['onFBResize', 'rw', 'func'], // onFBResize(rfb, width, height): frame buffer resized
|
||||
['onDesktopName', 'rw', 'func'], // onDesktopName(rfb, name): desktop name received
|
||||
['onXvpInit', 'rw', 'func'] // onXvpInit(version): XVP extensions active for this connection
|
||||
['onXvpInit', 'rw', 'func'], // onXvpInit(version): XVP extensions active for this connection
|
||||
['ast2100_onVideoSettingsChanged', 'rw', 'func'], // onVideoSettingsChanged(videoSettings): AST2100 video
|
||||
// quality settings changed in latest FBU.
|
||||
]);
|
||||
|
||||
RFB.prototype.set_local_cursor = function (cursor) {
|
||||
|
|
@ -2974,6 +3012,7 @@
|
|||
}
|
||||
|
||||
if (!this._aten_ast2100_dec) {
|
||||
var _rfb = this;
|
||||
var display = this._display;
|
||||
this._aten_ast2100_dec = new Ast2100Decoder({
|
||||
width: this._FBU.width,
|
||||
|
|
@ -2985,6 +3024,9 @@
|
|||
// the block to be enqueued instead of being blitted right
|
||||
// away via a call to _rgbxImageData().
|
||||
display.blitRgbxImage(x, y, width, height, buf, 0, true);
|
||||
},
|
||||
videoSettingsChangedCallback: function (settings) {
|
||||
_rfb._ast2100_onVideoSettingsChanged(settings);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
32
vnc.html
32
vnc.html
|
|
@ -237,6 +237,38 @@
|
|||
</li>
|
||||
</ul></div>
|
||||
</li>
|
||||
|
||||
<!--
|
||||
AST2100 (0x57) video encoding only:
|
||||
- Video quality slider (quantization table selector)
|
||||
- Subsampling mode
|
||||
-->
|
||||
<div id="noVNC_ast2100_settings" class="">
|
||||
<li><hr></li>
|
||||
<li>
|
||||
<div class="noVNC_expander">AST2100 Settings</div>
|
||||
<div><ul>
|
||||
|
||||
<li>
|
||||
<label>Video quality</label>
|
||||
<div class="slider-area">
|
||||
low
|
||||
<input type="range" min="0" max="11" class="slider-bar" id="noVNC_setting_ast2100_quality" />
|
||||
high
|
||||
</div>
|
||||
</li>
|
||||
<li><hr></li>
|
||||
<li>
|
||||
<label>Subsampling mode</label>
|
||||
<select id="noVNC_setting_ast2100_subsampling">
|
||||
<option value="444">4:4:4 (no subsampling; higher quality)</option>
|
||||
<option value="422">4:2:0 (chroma subsampling; lower quality)</option>
|
||||
</select>
|
||||
</li>
|
||||
</ul></div>
|
||||
</li>
|
||||
</div>
|
||||
|
||||
<li><hr></li>
|
||||
<li>
|
||||
<input type="button" id="noVNC_settings_apply" value="Apply" class="noVNC_submit" />
|
||||
|
|
|
|||
|
|
@ -267,6 +267,8 @@
|
|||
'shared': WebUtil.getConfigVar('shared', true),
|
||||
'view_only': WebUtil.getConfigVar('view_only', false),
|
||||
'onNotification': notification,
|
||||
'ast2100_quality': WebUtil.getConfigVar('ast2100_quality', -1),
|
||||
'ast2100_subsamplingMode': WebUtil.getConfigVar('ast2100_subsamplingMode', -1),
|
||||
'onUpdateState': updateState,
|
||||
'onDisconnected': disconnected,
|
||||
'onXvpInit': xvpInit,
|
||||
|
|
|
|||
Loading…
Reference in New Issue