Merge pull request #21 from kasmtech/feature/KASM-2001_mobile_keyboard
KASM-2001 Add keyboard controls panel
This commit is contained in:
commit
b5a1586c0a
|
@ -971,6 +971,147 @@ select:active {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* prevent selection */
|
||||||
|
body {
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
.keyboard-controls {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 5%;
|
||||||
|
right: 5%;
|
||||||
|
z-index: 100000;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .buttons {
|
||||||
|
display: none;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .buttons .button {
|
||||||
|
border-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .buttons .button:first-of-type {
|
||||||
|
border-top-left-radius: 6px;
|
||||||
|
border-top-right-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .button {
|
||||||
|
display: inline-block;
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
|
||||||
|
background-color:rgb(24, 31, 71);
|
||||||
|
border: 6px rgb(24, 31, 71) solid;
|
||||||
|
|
||||||
|
border-radius: 6px;
|
||||||
|
outline: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .button.ctrl {
|
||||||
|
background-image: url("../images/ctrl.svg");
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .button.alt {
|
||||||
|
background-image: url("../images/alt.svg");
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .button.windows {
|
||||||
|
background-size: contain;
|
||||||
|
background-image: url("../images/windows.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .button.tab {
|
||||||
|
background-size: contain;
|
||||||
|
background-image: url("../images/tab.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .button.escape {
|
||||||
|
background-size: contain;
|
||||||
|
background-image: url("../images/esc.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .button.ctrlaltdel {
|
||||||
|
background-size: contain;
|
||||||
|
background-image: url("../images/ctrlaltdel.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .button.keyboard {
|
||||||
|
background-size: contain;
|
||||||
|
background-image: url("../images/keyboard.svg");
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls .button.selected {
|
||||||
|
filter: brightness(150%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls.is-visible {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls.is-open .button.handle {
|
||||||
|
border-top-left-radius: 0px;
|
||||||
|
border-top-right-radius: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls.is-open .buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
animation-name: showKeyboardControls;
|
||||||
|
animation-duration: 0.2s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.keyboard-controls.was-open .buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
animation-name: hideKeyboardControls;
|
||||||
|
animation-duration: 0.2s;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
animation-fill-mode: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes showKeyboardControls {
|
||||||
|
0% {
|
||||||
|
transform: scale(1, 0.2);
|
||||||
|
transform-origin: bottom center;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1, 1);
|
||||||
|
transform-origin: bottom center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes hideKeyboardControls {
|
||||||
|
0% {
|
||||||
|
transform: scale(1, 1);
|
||||||
|
transform-origin: bottom center;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1, 0);
|
||||||
|
transform-origin: bottom center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
* Media sizing
|
* Media sizing
|
||||||
* ----------------------------------------
|
* ----------------------------------------
|
||||||
|
|
111
app/ui.js
111
app/ui.js
|
@ -30,11 +30,19 @@ window.updateSetting = (name, value) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.showKeyboardControlsPanel = () => {
|
||||||
|
document.querySelector(".keyboard-controls").classList.add("is-visible");
|
||||||
|
}
|
||||||
|
|
||||||
|
window.hideKeyboardControlsPanel = () => {
|
||||||
|
document.querySelector(".keyboard-controls").classList.remove("is-visible");
|
||||||
|
}
|
||||||
|
|
||||||
import "core-js/stable";
|
import "core-js/stable";
|
||||||
import "regenerator-runtime/runtime";
|
import "regenerator-runtime/runtime";
|
||||||
import * as Log from '../core/util/logging.js';
|
import * as Log from '../core/util/logging.js';
|
||||||
import _, { l10n } from './localization.js';
|
import _, { l10n } from './localization.js';
|
||||||
import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold, supportsBinaryClipboard, isFirefox }
|
import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold, supportsBinaryClipboard, isFirefox, isWindows, isIOS }
|
||||||
from '../core/util/browser.js';
|
from '../core/util/browser.js';
|
||||||
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
||||||
import KeyTable from "../core/input/keysym.js";
|
import KeyTable from "../core/input/keysym.js";
|
||||||
|
@ -125,6 +133,7 @@ const UI = {
|
||||||
UI.initFullscreen();
|
UI.initFullscreen();
|
||||||
|
|
||||||
// Setup event handlers
|
// Setup event handlers
|
||||||
|
UI.addKeyboardControlsPanelHandlers();
|
||||||
UI.addControlbarHandlers();
|
UI.addControlbarHandlers();
|
||||||
UI.addTouchSpecificHandlers();
|
UI.addTouchSpecificHandlers();
|
||||||
UI.addExtraKeysHandlers();
|
UI.addExtraKeysHandlers();
|
||||||
|
@ -154,6 +163,14 @@ const UI = {
|
||||||
UI.openConnectPanel();
|
UI.openConnectPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !isWindows() && (
|
||||||
|
(window.parent.KASM_INITIAL_KEYBOARD_CONTROLS_MODE === "on") ||
|
||||||
|
(window.parent.KASM_INITIAL_KEYBOARD_CONTROLS_MODE === "auto" && isTouchDevice)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
showKeyboardControlsPanel();
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.resolve(UI.rfb);
|
return Promise.resolve(UI.rfb);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -272,6 +289,51 @@ const UI = {
|
||||||
* EVENT HANDLERS
|
* EVENT HANDLERS
|
||||||
* ------v------*/
|
* ------v------*/
|
||||||
|
|
||||||
|
addKeyboardControlsPanelHandlers() {
|
||||||
|
// panel dragging
|
||||||
|
interact(".keyboard-controls").draggable({
|
||||||
|
allowFrom: ".handle",
|
||||||
|
listeners: {
|
||||||
|
move: (e) => {
|
||||||
|
const target = e.target;
|
||||||
|
const x = (parseFloat(target.getAttribute("data-x")) || 0) + e.dx;
|
||||||
|
const y = (parseFloat(target.getAttribute("data-y")) || 0) + e.dy;
|
||||||
|
target.style.transform = `translate(${x}px, ${y}px)`;
|
||||||
|
target.setAttribute("data-x", x);
|
||||||
|
target.setAttribute("data-y", y);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// panel expanding
|
||||||
|
interact(".keyboard-controls .handle")
|
||||||
|
.pointerEvents({ holdDuration: 350 })
|
||||||
|
.on("hold", (e) => {
|
||||||
|
const buttonsEl = document.querySelector(".keyboard-controls");
|
||||||
|
|
||||||
|
const isOpen = buttonsEl.classList.contains("is-open");
|
||||||
|
buttonsEl.classList.toggle("was-open", isOpen);
|
||||||
|
buttonsEl.classList.toggle("is-open", !isOpen);
|
||||||
|
|
||||||
|
setTimeout(() => buttonsEl.classList.remove("was-open"), 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
// keyboard showing
|
||||||
|
interact(".keyboard-controls .handle").on("tap", (e) => {
|
||||||
|
if (e.dt < 150) {
|
||||||
|
UI.toggleVirtualKeyboard();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// panel buttons
|
||||||
|
interact(".keyboard-controls .button.ctrl").on("tap", UI.toggleCtrl);
|
||||||
|
interact(".keyboard-controls .button.alt").on("tap", UI.toggleAlt);
|
||||||
|
interact(".keyboard-controls .button.windows").on("tap", UI.toggleWindows);
|
||||||
|
interact(".keyboard-controls .button.tab").on("tap", UI.sendTab);
|
||||||
|
interact(".keyboard-controls .button.escape").on("tap", UI.sendEsc);
|
||||||
|
interact(".keyboard-controls .button.ctrlaltdel").on("tap", UI.sendCtrlAltDel);
|
||||||
|
},
|
||||||
|
|
||||||
addControlbarHandlers() {
|
addControlbarHandlers() {
|
||||||
document.getElementById("noVNC_control_bar")
|
document.getElementById("noVNC_control_bar")
|
||||||
.addEventListener('mousemove', UI.activateControlbar);
|
.addEventListener('mousemove', UI.activateControlbar);
|
||||||
|
@ -1268,13 +1330,17 @@ const UI = {
|
||||||
UI.rfb.addEventListener("capabilities", UI.updatePowerButton);
|
UI.rfb.addEventListener("capabilities", UI.updatePowerButton);
|
||||||
UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
|
UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
|
||||||
UI.rfb.addEventListener("bottleneck_stats", UI.bottleneckStatsRecieve);
|
UI.rfb.addEventListener("bottleneck_stats", UI.bottleneckStatsRecieve);
|
||||||
document.addEventListener('mouseenter', UI.enterVNC);
|
|
||||||
document.addEventListener('mouseleave', UI.leaveVNC);
|
if (!isTouchDevice) {
|
||||||
document.addEventListener('blur', UI.blurVNC);
|
document.addEventListener('mouseenter', UI.enterVNC);
|
||||||
document.addEventListener('focus', UI.focusVNC);
|
document.addEventListener('mouseleave', UI.leaveVNC);
|
||||||
document.addEventListener('focusout', UI.focusoutVNC);
|
document.addEventListener('focusout', UI.focusoutVNC);
|
||||||
document.addEventListener('mousemove', UI.mouseMoveVNC);
|
document.addEventListener('mousemove', UI.mouseMoveVNC);
|
||||||
document.addEventListener('mousedown', UI.mouseDownVNC);
|
document.addEventListener('mousedown', UI.mouseDownVNC);
|
||||||
|
document.addEventListener('blur', UI.blurVNC);
|
||||||
|
document.addEventListener('focus', UI.focusVNC);
|
||||||
|
}
|
||||||
|
|
||||||
UI.rfb.addEventListener("bell", UI.bell);
|
UI.rfb.addEventListener("bell", UI.bell);
|
||||||
UI.rfb.addEventListener("desktopname", UI.updateDesktopName);
|
UI.rfb.addEventListener("desktopname", UI.updateDesktopName);
|
||||||
UI.rfb.translateShortcuts = UI.getSetting('translate_shortcuts');
|
UI.rfb.translateShortcuts = UI.getSetting('translate_shortcuts');
|
||||||
|
@ -1849,8 +1915,6 @@ const UI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
showVirtualKeyboard() {
|
showVirtualKeyboard() {
|
||||||
if (!isTouchDevice) return;
|
|
||||||
|
|
||||||
const input = document.getElementById('noVNC_keyboardinput');
|
const input = document.getElementById('noVNC_keyboardinput');
|
||||||
|
|
||||||
if (document.activeElement == input) return;
|
if (document.activeElement == input) return;
|
||||||
|
@ -1864,11 +1928,17 @@ const UI = {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// setSelectionRange is undefined in Google Chrome
|
// setSelectionRange is undefined in Google Chrome
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ensure that the hidden input used for showing the virutal keyboard
|
||||||
|
// does not steal focus if the user has closed it manually
|
||||||
|
document.querySelector("canvas").addEventListener("touchstart", () => {
|
||||||
|
if (document.activeElement === input) {
|
||||||
|
input.blur();
|
||||||
|
}
|
||||||
|
}, { once: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
hideVirtualKeyboard() {
|
hideVirtualKeyboard() {
|
||||||
if (!isTouchDevice) return;
|
|
||||||
|
|
||||||
const input = document.getElementById('noVNC_keyboardinput');
|
const input = document.getElementById('noVNC_keyboardinput');
|
||||||
|
|
||||||
if (document.activeElement != input) return;
|
if (document.activeElement != input) return;
|
||||||
|
@ -2025,6 +2095,14 @@ const UI = {
|
||||||
.classList.add("noVNC_selected");
|
.classList.add("noVNC_selected");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
disableSoftwareKeyboard() {
|
||||||
|
document.querySelector("#noVNC_keyboard_button").disabled = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
enableSoftwareKeyboard() {
|
||||||
|
document.querySelector("#noVNC_keyboard_button").disabled = false;
|
||||||
|
},
|
||||||
|
|
||||||
closeExtraKeys() {
|
closeExtraKeys() {
|
||||||
document.getElementById('noVNC_modifiers')
|
document.getElementById('noVNC_modifiers')
|
||||||
.classList.remove("noVNC_open");
|
.classList.remove("noVNC_open");
|
||||||
|
@ -2033,8 +2111,7 @@ const UI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleExtraKeys() {
|
toggleExtraKeys() {
|
||||||
if (document.getElementById('noVNC_modifiers')
|
if (document.getElementById('noVNC_modifiers').classList.contains("noVNC_open")) {
|
||||||
.classList.contains("noVNC_open")) {
|
|
||||||
UI.closeExtraKeys();
|
UI.closeExtraKeys();
|
||||||
} else {
|
} else {
|
||||||
UI.openExtraKeys();
|
UI.openExtraKeys();
|
||||||
|
@ -2058,6 +2135,8 @@ const UI = {
|
||||||
UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
|
UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
|
||||||
btn.classList.add("noVNC_selected");
|
btn.classList.add("noVNC_selected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.querySelector(".keyboard-controls .button.ctrl").classList.toggle("selected");
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleWindows() {
|
toggleWindows() {
|
||||||
|
@ -2069,6 +2148,8 @@ const UI = {
|
||||||
UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", true);
|
UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", true);
|
||||||
btn.classList.add("noVNC_selected");
|
btn.classList.add("noVNC_selected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.querySelector(".keyboard-controls .button.windows").classList.toggle("selected");
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleAlt() {
|
toggleAlt() {
|
||||||
|
@ -2080,6 +2161,8 @@ const UI = {
|
||||||
UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
|
UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
|
||||||
btn.classList.add("noVNC_selected");
|
btn.classList.add("noVNC_selected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.querySelector(".keyboard-controls .button.alt").classList.toggle("selected");
|
||||||
},
|
},
|
||||||
|
|
||||||
sendCtrlAltDel() {
|
sendCtrlAltDel() {
|
||||||
|
|
20
core/rfb.js
20
core/rfb.js
|
@ -11,7 +11,7 @@ import { toUnsigned32bit, toSigned32bit } from './util/int.js';
|
||||||
import * as Log from './util/logging.js';
|
import * as Log from './util/logging.js';
|
||||||
import { encodeUTF8, decodeUTF8 } from './util/strings.js';
|
import { encodeUTF8, decodeUTF8 } from './util/strings.js';
|
||||||
import { hashUInt8Array } from './util/int.js';
|
import { hashUInt8Array } from './util/int.js';
|
||||||
import { dragThreshold, supportsCursorURIs, isTouchDevice, isWindows, isMac } from './util/browser.js';
|
import { dragThreshold, supportsCursorURIs, isTouchDevice, isWindows, isMac, isIOS } from './util/browser.js';
|
||||||
import { clientToElement } from './util/element.js';
|
import { clientToElement } from './util/element.js';
|
||||||
import { setCapture } from './util/events.js';
|
import { setCapture } from './util/events.js';
|
||||||
import EventTargetMixin from './util/eventtarget.js';
|
import EventTargetMixin from './util/eventtarget.js';
|
||||||
|
@ -192,6 +192,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
|
|
||||||
// Bound event handlers
|
// Bound event handlers
|
||||||
this._eventHandlers = {
|
this._eventHandlers = {
|
||||||
|
updateHiddenKeyboard: this._updateHiddenKeyboard.bind(this),
|
||||||
focusCanvas: this._focusCanvas.bind(this),
|
focusCanvas: this._focusCanvas.bind(this),
|
||||||
windowResize: this._windowResize.bind(this),
|
windowResize: this._windowResize.bind(this),
|
||||||
handleMouse: this._handleMouse.bind(this),
|
handleMouse: this._handleMouse.bind(this),
|
||||||
|
@ -894,6 +895,15 @@ export default class RFB extends EventTargetMixin {
|
||||||
this._canvas.addEventListener("mousedown", this._eventHandlers.focusCanvas);
|
this._canvas.addEventListener("mousedown", this._eventHandlers.focusCanvas);
|
||||||
this._canvas.addEventListener("touchstart", this._eventHandlers.focusCanvas);
|
this._canvas.addEventListener("touchstart", this._eventHandlers.focusCanvas);
|
||||||
|
|
||||||
|
// In order for the keyboard to not occlude the input being edited
|
||||||
|
// we move the hidden input we use for triggering the keyboard to the last click
|
||||||
|
// position which should trigger a page being moved down enough
|
||||||
|
// to show the input. On Android the whole website gets resized so we don't
|
||||||
|
// have to do anything.
|
||||||
|
if (isIOS()) {
|
||||||
|
this._canvas.addEventListener("touchend", this._eventHandlers.updateHiddenKeyboard);
|
||||||
|
}
|
||||||
|
|
||||||
// Mouse events
|
// Mouse events
|
||||||
this._canvas.addEventListener('mousedown', this._eventHandlers.handleMouse);
|
this._canvas.addEventListener('mousedown', this._eventHandlers.handleMouse);
|
||||||
this._canvas.addEventListener('mouseup', this._eventHandlers.handleMouse);
|
this._canvas.addEventListener('mouseup', this._eventHandlers.handleMouse);
|
||||||
|
@ -948,6 +958,12 @@ export default class RFB extends EventTargetMixin {
|
||||||
Log.Debug("<< RFB.disconnect");
|
Log.Debug("<< RFB.disconnect");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updateHiddenKeyboard(event) {
|
||||||
|
// On iOS 15 the navigation bar is at the bottom so we need to account for it
|
||||||
|
const y = Math.max(0, event.pageY - 50);
|
||||||
|
document.querySelector("#noVNC_keyboardinput").style.top = `${y}px`;
|
||||||
|
}
|
||||||
|
|
||||||
_focusCanvas(event) {
|
_focusCanvas(event) {
|
||||||
// Hack:
|
// Hack:
|
||||||
// On most mobile phones it's only possible to play audio
|
// On most mobile phones it's only possible to play audio
|
||||||
|
@ -955,7 +971,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
// impossible to listen for touch events on child frames (only on mobile phones)
|
// impossible to listen for touch events on child frames (only on mobile phones)
|
||||||
// we delegate the audio unlocking to the parent window.
|
// we delegate the audio unlocking to the parent window.
|
||||||
if (window.parent && !window.parent.KASM_AUDIO_UNLOCKED) {
|
if (window.parent && !window.parent.KASM_AUDIO_UNLOCKED) {
|
||||||
window.parent.unlockAudio();
|
window.parent.unlockAudio && window.parent.unlockAudio();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.focusOnClick) {
|
if (!this.focusOnClick) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
69
vnc.html
69
vnc.html
|
@ -47,6 +47,8 @@
|
||||||
<link rel="apple-touch-icon" sizes="120x120" type="image/png" href="app/images/icons/368_kasm_logo_only_120x120.png">
|
<link rel="apple-touch-icon" sizes="120x120" type="image/png" href="app/images/icons/368_kasm_logo_only_120x120.png">
|
||||||
<link rel="apple-touch-icon" sizes="152x152" type="image/png" href="app/images/icons/368_kasm_logo_only_152x152.png">
|
<link rel="apple-touch-icon" sizes="152x152" type="image/png" href="app/images/icons/368_kasm_logo_only_152x152.png">
|
||||||
|
|
||||||
|
<script src="vendor/interact.min.js"></script>
|
||||||
|
|
||||||
<!-- Stylesheets -->
|
<!-- Stylesheets -->
|
||||||
<!--link rel="stylesheet" href="app/styles/base.css">
|
<!--link rel="stylesheet" href="app/styles/base.css">
|
||||||
|
|
||||||
|
@ -84,6 +86,31 @@
|
||||||
Loading statistics...
|
Loading statistics...
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="noVNC_vcenter">
|
||||||
|
<div id="noVNC_modifiers" class="noVNC_panel">
|
||||||
|
<input type="image" alt="Keyboard" src="app/images/keyboard.svg"
|
||||||
|
id="noVNC_keyboard_button" class="noVNC_button" title="Show Keyboard">
|
||||||
|
<input type="image" alt="Ctrl" src="app/images/ctrl.svg"
|
||||||
|
id="noVNC_toggle_ctrl_button" class="noVNC_button"
|
||||||
|
title="Toggle Ctrl">
|
||||||
|
<input type="image" alt="Alt" src="app/images/alt.svg"
|
||||||
|
id="noVNC_toggle_alt_button" class="noVNC_button"
|
||||||
|
title="Toggle Alt">
|
||||||
|
<input type="image" alt="Windows" src="app/images/windows.svg"
|
||||||
|
id="noVNC_toggle_windows_button" class="noVNC_button"
|
||||||
|
title="Toggle Windows">
|
||||||
|
<input type="image" alt="Tab" src="app/images/tab.svg"
|
||||||
|
id="noVNC_send_tab_button" class="noVNC_button"
|
||||||
|
title="Send Tab">
|
||||||
|
<input type="image" alt="Esc" src="app/images/esc.svg"
|
||||||
|
id="noVNC_send_esc_button" class="noVNC_button"
|
||||||
|
title="Send Escape">
|
||||||
|
<input type="image" alt="Ctrl+Alt+Del" src="app/images/ctrlaltdel.svg"
|
||||||
|
id="noVNC_send_ctrl_alt_del_button" class="noVNC_button"
|
||||||
|
title="Send Ctrl-Alt-Del">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- noVNC Control Bar -->
|
<!-- noVNC Control Bar -->
|
||||||
<div id="noVNC_control_bar_anchor" class="noVNC_vcenter">
|
<div id="noVNC_control_bar_anchor" class="noVNC_vcenter">
|
||||||
|
|
||||||
|
@ -100,37 +127,10 @@
|
||||||
title="Move/Drag Viewport">
|
title="Move/Drag Viewport">
|
||||||
|
|
||||||
<!--noVNC Touch Device only buttons-->
|
<!--noVNC Touch Device only buttons-->
|
||||||
<div id="noVNC_mobile_buttons">
|
|
||||||
<input type="image" alt="Keyboard" src="app/images/keyboard.svg"
|
|
||||||
id="noVNC_keyboard_button" class="noVNC_button" title="Show Keyboard">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Extra manual keys -->
|
<!-- Extra manual keys -->
|
||||||
<input type="image" alt="Extra keys" src="app/images/toggleextrakeys.svg"
|
<input type="image" alt="Extra keys" src="app/images/toggleextrakeys.svg"
|
||||||
id="noVNC_toggle_extra_keys_button" class="noVNC_button"
|
id="noVNC_toggle_extra_keys_button" class="noVNC_button"
|
||||||
title="Show Extra Keys">
|
title="Show Extra Keys">
|
||||||
<div class="noVNC_vcenter">
|
|
||||||
<div id="noVNC_modifiers" class="noVNC_panel">
|
|
||||||
<input type="image" alt="Ctrl" src="app/images/ctrl.svg"
|
|
||||||
id="noVNC_toggle_ctrl_button" class="noVNC_button"
|
|
||||||
title="Toggle Ctrl">
|
|
||||||
<input type="image" alt="Alt" src="app/images/alt.svg"
|
|
||||||
id="noVNC_toggle_alt_button" class="noVNC_button"
|
|
||||||
title="Toggle Alt">
|
|
||||||
<input type="image" alt="Windows" src="app/images/windows.svg"
|
|
||||||
id="noVNC_toggle_windows_button" class="noVNC_button"
|
|
||||||
title="Toggle Windows">
|
|
||||||
<input type="image" alt="Tab" src="app/images/tab.svg"
|
|
||||||
id="noVNC_send_tab_button" class="noVNC_button"
|
|
||||||
title="Send Tab">
|
|
||||||
<input type="image" alt="Esc" src="app/images/esc.svg"
|
|
||||||
id="noVNC_send_esc_button" class="noVNC_button"
|
|
||||||
title="Send Escape">
|
|
||||||
<input type="image" alt="Ctrl+Alt+Del" src="app/images/ctrlaltdel.svg"
|
|
||||||
id="noVNC_send_ctrl_alt_del_button" class="noVNC_button"
|
|
||||||
title="Send Ctrl-Alt-Del">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Shutdown/Reboot -->
|
<!-- Shutdown/Reboot -->
|
||||||
<input type="image" alt="Shutdown/Reboot" src="app/images/power.svg"
|
<input type="image" alt="Shutdown/Reboot" src="app/images/power.svg"
|
||||||
|
@ -443,5 +443,18 @@
|
||||||
<source src="app/sounds/bell.oga" type="audio/ogg">
|
<source src="app/sounds/bell.oga" type="audio/ogg">
|
||||||
<source src="app/sounds/bell.mp3" type="audio/mpeg">
|
<source src="app/sounds/bell.mp3" type="audio/mpeg">
|
||||||
</audio>
|
</audio>
|
||||||
|
|
||||||
|
<div class="keyboard-controls">
|
||||||
|
<div class="buttons">
|
||||||
|
<div class="button ctrl"></div>
|
||||||
|
<div class="button alt"></div>
|
||||||
|
<div class="button windows"></div>
|
||||||
|
<div class="button tab"></div>
|
||||||
|
<div class="button escape"></div>
|
||||||
|
<div class="button ctrlaltdel"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button keyboard handle"></div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Reference in New Issue