summaryrefslogtreecommitdiff
path: root/kvmd
diff options
context:
space:
mode:
authorDevaev Maxim <[email protected]>2018-08-08 08:38:47 +0300
committerDevaev Maxim <[email protected]>2018-08-08 08:38:54 +0300
commit3551ebd9a17cf150d95555ba3ff234910e28a427 (patch)
tree0e097d880cd295f994d1d3a5ccf35ddec65df945 /kvmd
parentd9d433025c70621de888bd5dcf3b23b5c29790b3 (diff)
paste-as-keys
Diffstat (limited to 'kvmd')
-rw-r--r--kvmd/eslintrc.yaml1
-rw-r--r--kvmd/web/css/leds.css3
-rw-r--r--kvmd/web/index.html3
-rw-r--r--kvmd/web/js/hid.js130
-rw-r--r--kvmd/web/js/session.js4
-rw-r--r--kvmd/web/svg/gear-led.svg151
6 files changed, 276 insertions, 16 deletions
diff --git a/kvmd/eslintrc.yaml b/kvmd/eslintrc.yaml
index 68d1faf6..215105b0 100644
--- a/kvmd/eslintrc.yaml
+++ b/kvmd/eslintrc.yaml
@@ -12,6 +12,7 @@ globals:
env:
browser: true
+ es6: true
extends: "eslint:recommended"
diff --git a/kvmd/web/css/leds.css b/kvmd/web/css/leds.css
index 0dbe6fe1..6558eb35 100644
--- a/kvmd/web/css/leds.css
+++ b/kvmd/web/css/leds.css
@@ -25,7 +25,8 @@ img.led-hdd-busy {
filter: invert(0.5) sepia(1) saturate(15) hue-rotate(320deg);
}
-img.led-msd-writing {
+img.led-msd-writing,
+img.led-pak-typing {
-webkit-filter: invert(0.5) sepia(1) saturate(5) hue-rotate(0deg);
filter: invert(0.5) sepia(1) saturate(5) hue-rotate(0deg);
-webkit-animation: spin 2s linear infinite;
diff --git a/kvmd/web/index.html b/kvmd/web/index.html
index 9d87e392..0473bd4d 100644
--- a/kvmd/web/index.html
+++ b/kvmd/web/index.html
@@ -189,9 +189,12 @@
<li class="ctl-right-actions">
<div class="ctl-dropdown">
<a class="ctl-item" href="#">
+ <img data-dont-hide-menu id="pak-led" class="led-off" src="svg/gear-led.svg" />
Shortcuts &#8628;
</a>
<div class="ctl-dropdown-content">
+ <button disabled id="pak-button">&bull; Paste-as-Keys <sup><i>ascii-only</i></sup></button>
+ <hr>
<button onclick="hid.emitShortcut('ControlLeft', 'AltLeft', 'Delete');">&bull; Ctrl+Alt+Del</button>
<hr>
<button onclick="hid.emitShortcut('ControlLeft', 'KeyW');">&bull; Ctrl+W</button>
diff --git a/kvmd/web/js/hid.js b/kvmd/web/js/hid.js
index 130b66a8..86d3931d 100644
--- a/kvmd/web/js/hid.js
+++ b/kvmd/web/js/hid.js
@@ -1,7 +1,24 @@
var hid = new function() {
+ var __ws = null;
+ var __chars_to_codes = {};
+ var __codes_delay = 50;
+
this.init = function() {
keyboard.init();
mouse.init();
+ if (window.navigator.clipboard && window.navigator.clipboard.readText) {
+ __chars_to_codes = __buildCharsToCodes();
+ $("pak-button").onclick = __pasteAsKeys;
+ } else {
+ $("pak-button").title = "Your browser does not support the Clipboard API.\nUse Google Chrome or Chromium.";
+ }
+ };
+
+ this.setSocket = function(ws) {
+ __ws = ws;
+ keyboard.setSocket(ws);
+ mouse.setSocket(ws);
+ $("pak-button").disabled = !(window.navigator.clipboard && window.navigator.clipboard.readText && ws);
};
this.updateLeds = function() {
@@ -14,24 +31,111 @@ var hid = new function() {
};
this.emitShortcut = function(...codes) {
- tools.debug("Emitted keys:", codes);
- var delay = 0;
- [[codes, true], [codes.slice().reverse(), false]].forEach(function(op) {
- var [op_codes, state] = op;
- op_codes.forEach(function(code) {
- setTimeout(() => keyboard.fireEvent(code, state), delay);
- delay += 100;
+ return new Promise(function(resolve) {
+ tools.debug("Emitting keys:", codes);
+
+ var raw_events = [];
+ [[codes, true], [codes.slice().reverse(), false]].forEach(function(op) {
+ var [op_codes, state] = op;
+ op_codes.forEach(function(code) {
+ raw_events.push({code: code, state: state});
+ });
});
+
+ var index = 0;
+ var iterate = () => setTimeout(function() {
+ keyboard.fireEvent(raw_events[index].code, raw_events[index].state);
+ ++index;
+ if (index < raw_events.length) {
+ iterate();
+ } else {
+ resolve(null);
+ }
+ }, __codes_delay);
+ iterate();
});
};
- this.installCapture = function(ws) {
- keyboard.setSocket(ws);
- mouse.setSocket(ws);
+ var __buildCharsToCodes = function() {
+ var chars_to_codes = {
+ "\n": ["Enter"],
+ "\t": ["Tab"],
+ " ": ["Space"],
+ "`": ["Backquote"], "~": ["ShiftLeft", "Backquote"],
+ "\\": ["Backslash"], "|": ["ShiftLeft", "Backslash"],
+ "[": ["BracketLeft"], "{": ["ShiftLeft", "BracketLeft"],
+ "]": ["BracketLeft"], "}": ["ShiftLeft", "BracketRight"],
+ ",": ["Comma"], "<": ["ShiftLeft", "Comma"],
+ ".": ["Period"], ">": ["ShiftLeft", "Period"],
+ "1": ["Digit1"], "!": ["ShiftLeft", "Digit1"],
+ "2": ["Digit2"], "@": ["ShiftLeft", "Digit2"],
+ "3": ["Digit3"], "#": ["ShiftLeft", "Digit3"],
+ "4": ["Digit4"], "$": ["ShiftLeft", "Digit4"],
+ "5": ["Digit5"], "%": ["ShiftLeft", "Digit5"],
+ "6": ["Digit6"], "^": ["ShiftLeft", "Digit6"],
+ "7": ["Digit7"], "&": ["ShiftLeft", "Digit7"],
+ "8": ["Digit8"], "*": ["ShiftLeft", "Digit8"],
+ "9": ["Digit9"], "(": ["ShiftLeft", "Digit9"],
+ "0": ["Digit0"], ")": ["ShiftLeft", "Digit0"],
+ "-": ["Minus"], "_": ["ShiftLeft", "Minus"],
+ "'": ["Quote"], "\"": ["ShiftLeft", "Quote"],
+ ";": ["Semicolon"], ":": ["ShiftLeft", "Semicolon"],
+ "/": ["Slash"], "?": ["ShiftLeft", "Slash"],
+ "=": ["Equal"], "+": ["ShiftLeft", "Equal"],
+ };
+
+ for (var ch = "a".charCodeAt(0); ch <= "z".charCodeAt(0); ++ch) {
+ var low = String.fromCharCode(ch);
+ var up = low.toUpperCase();
+ var code = "Key" + up;
+ chars_to_codes[low] = [code];
+ chars_to_codes[up] = ["ShiftLeft", code];
+ }
+
+ return chars_to_codes;
};
- this.clearCapture = function() {
- mouse.setSocket(null);
- keyboard.setSocket(null);
+ var __pasteAsKeys = function() {
+ window.navigator.clipboard.readText().then(function(clipboard_text) {
+ clipboard_text = clipboard_text.replace(/[^\x00-\x7F]/g, ""); // eslint-disable-line no-control-regex
+ if (clipboard_text) {
+ var clipboard_codes = [];
+ var codes_count = 0;
+ [...clipboard_text].forEach(function(ch) {
+ var codes = __chars_to_codes[ch];
+ if (codes) {
+ codes_count += codes.length;
+ clipboard_codes.push(codes);
+ }
+ });
+
+ var confirm_msg = (
+ "You are going to automatically type " + codes_count
+ + " characters from the system clipboard.\nAre you sure you want to continue?\n"
+ + "It will take " + (__codes_delay * codes_count * 2 / 1000) + " seconds."
+ );
+
+ if (codes_count <= 256 || confirm(confirm_msg)) {
+ $("pak-button").disabled = true;
+ $("pak-led").className = "led-pak-typing";
+
+ tools.debug("Paste-as-keys:", clipboard_text);
+
+ var index = 0;
+ var iterate = function() {
+ hid.emitShortcut(...clipboard_codes[index]).then(function() {
+ ++index;
+ if (index < clipboard_codes.length && __ws) {
+ iterate();
+ } else {
+ $("pak-button").disabled = false;
+ $("pak-led").className = "led-off";
+ }
+ });
+ };
+ iterate();
+ }
+ }
+ });
};
};
diff --git a/kvmd/web/js/session.js b/kvmd/web/js/session.js
index d1d301bd..238ff877 100644
--- a/kvmd/web/js/session.js
+++ b/kvmd/web/js/session.js
@@ -32,7 +32,7 @@ var session = new function() {
tools.debug("WebSocket opened:", event);
atx.loadInitialState();
msd.loadInitialState();
- hid.installCapture(__ws);
+ hid.setSocket(__ws);
__missed_heartbeats = 0;
__ping_timer = setInterval(__pingServer, 1000);
};
@@ -69,7 +69,7 @@ var session = new function() {
clearInterval(__ping_timer);
__ping_timer = null;
}
- hid.clearCapture();
+ hid.setSocket(null);
atx.clearState();
__ws = null;
setTimeout(session.startPoller, 1000);
diff --git a/kvmd/web/svg/gear-led.svg b/kvmd/web/svg/gear-led.svg
new file mode 100644
index 00000000..db9aff21
--- /dev/null
+++ b/kvmd/web/svg/gear-led.svg
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 521.99903 521.99702"
+ xml:space="preserve"
+ sodipodi:docname="gear-led-2.svg"
+ inkscape:version="0.92.2 2405546, 2018-03-11"
+ width="521.99902"
+ height="521.99701"><metadata
+ id="metadata49"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
+ id="defs47">
+
+</defs><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1020"
+ id="namedview45"
+ showgrid="false"
+ inkscape:zoom="0.92187681"
+ inkscape:cx="199.23524"
+ inkscape:cy="241.2828"
+ inkscape:window-x="0"
+ inkscape:window-y="30"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="Layer_1"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<g
+ id="g4810"><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g4">
+ <path
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 489.175,206.556 c -9.629,-1.442 -19.514,-2.825 -29.379,-4.111 -1.195,-0.155 -2.165,-0.966 -2.467,-2.064 -4.553,-16.523 -11.16,-32.467 -19.636,-47.389 -0.57,-1.002 -0.463,-2.266 0.273,-3.223 6.067,-7.885 12.081,-15.856 17.876,-23.69 7.824,-10.578 6.688,-25.588 -2.64,-34.917 L 420.836,58.796 c -9.329,-9.328 -24.338,-10.464 -34.918,-2.638 -7.817,5.782 -15.787,11.796 -23.689,17.875 -0.954,0.736 -2.221,0.843 -3.223,0.274 -14.921,-8.476 -30.865,-15.083 -47.389,-19.637 -1.099,-0.301 -1.91,-1.271 -2.066,-2.469 -1.289,-9.88 -2.671,-19.764 -4.109,-29.376 C 303.495,9.812 292.079,0 278.886,0 h -45.773 c -13.194,0 -24.61,9.812 -26.554,22.824 -1.439,9.614 -2.821,19.497 -4.11,29.379 -0.157,1.197 -0.967,2.165 -2.067,2.467 -16.524,4.556 -32.469,11.162 -47.387,19.637 -1.003,0.569 -2.269,0.459 -3.225,-0.274 C 141.869,67.954 133.898,61.94 126.08,56.157 115.499,48.332 100.49,49.47 91.163,58.797 L 58.797,91.163 c -9.329,9.33 -10.464,24.341 -2.638,34.918 5.804,7.846 11.818,15.815 17.875,23.688 0.735,0.955 0.843,2.22 0.274,3.223 -8.478,14.925 -15.084,30.869 -19.637,47.389 -0.301,1.097 -1.271,1.908 -2.467,2.065 -9.86,1.287 -19.744,2.669 -29.378,4.111 C 9.812,208.502 0,219.92 0,233.112 v 45.774 c 0,13.193 9.812,24.61 22.824,26.556 9.634,1.442 19.519,2.824 29.379,4.11 1.197,0.157 2.165,0.967 2.467,2.066 4.553,16.521 11.16,32.465 19.637,47.389 0.569,1.003 0.461,2.268 -0.274,3.223 -6.072,7.892 -12.086,15.862 -17.875,23.689 -7.825,10.578 -6.691,25.589 2.638,34.918 l 32.366,32.366 c 9.33,9.329 24.341,10.465 34.918,2.638 7.817,-5.782 15.787,-11.796 23.689,-17.875 0.955,-0.736 2.221,-0.842 3.223,-0.274 14.92,8.476 30.863,15.081 47.389,19.637 1.099,0.302 1.91,1.271 2.066,2.467 1.289,9.88 2.672,19.765 4.11,29.376 1.946,13.013 13.362,22.825 26.556,22.825 h 45.773 c 13.193,0 24.61,-9.812 26.555,-22.827 1.439,-9.623 2.821,-19.507 4.109,-29.376 0.157,-1.197 0.967,-2.166 2.066,-2.469 16.524,-4.556 32.469,-11.162 47.388,-19.637 1.003,-0.567 2.268,-0.459 3.224,0.274 7.901,6.079 15.872,12.093 23.689,17.875 10.578,7.825 25.588,6.691 34.918,-2.638 l 32.366,-32.366 c 9.328,-9.329 10.464,-24.339 2.639,-34.918 -5.795,-7.831 -11.81,-15.802 -17.876,-23.689 -0.735,-0.955 -0.843,-2.22 -0.273,-3.223 8.477,-14.924 15.083,-30.868 19.636,-47.388 0.304,-1.1 1.272,-1.91 2.469,-2.067 9.863,-1.286 19.748,-2.669 29.378,-4.11 13.013,-1.945 22.825,-13.362 22.825,-26.555 v -45.774 c 0,-13.19 -9.812,-24.608 -22.824,-26.553 z m -1.084,72.332 c 0,1.45 -1.054,2.7 -2.453,2.911 -9.482,1.419 -19.216,2.779 -28.932,4.048 -10.758,1.402 -19.56,9.024 -22.426,19.42 -4.029,14.618 -9.875,28.727 -17.375,41.932 -5.333,9.389 -4.504,21.012 2.112,29.612 5.976,7.768 11.899,15.617 17.604,23.329 0.842,1.137 0.702,2.769 -0.323,3.794 L 403.931,436.3 c -1.026,1.026 -2.657,1.163 -3.793,0.324 -7.697,-5.695 -15.548,-11.618 -23.33,-17.605 -8.599,-6.617 -20.221,-7.446 -29.609,-2.114 -13.205,7.5 -27.314,13.347 -41.934,17.377 -10.394,2.865 -18.016,11.667 -19.421,22.426 -1.267,9.722 -2.629,19.456 -4.047,28.932 -0.209,1.399 -1.461,2.453 -2.911,2.453 h -45.773 c -1.45,0 -2.702,-1.054 -2.911,-2.454 -1.415,-9.465 -2.778,-19.199 -4.047,-28.93 -1.403,-10.759 -9.027,-19.561 -19.421,-22.426 -14.621,-4.03 -28.73,-9.877 -41.934,-17.378 -4.117,-2.337 -8.664,-3.491 -13.196,-3.491 -5.804,0 -11.585,1.89 -16.412,5.607 -7.783,5.987 -15.633,11.91 -23.33,17.605 -1.138,0.839 -2.767,0.702 -3.792,-0.324 L 75.703,403.936 c -1.026,-1.026 -1.166,-2.656 -0.324,-3.793 5.701,-7.707 11.623,-15.556 17.604,-23.33 6.615,-8.6 7.445,-20.221 2.114,-29.609 C 87.594,333.995 81.749,319.887 77.72,305.27 74.855,294.876 66.053,287.253 55.295,285.85 c -9.712,-1.267 -19.447,-2.63 -28.934,-4.048 -1.399,-0.21 -2.453,-1.461 -2.453,-2.911 v -45.774 c 0,-1.45 1.054,-2.701 2.453,-2.911 9.487,-1.419 19.221,-2.781 28.932,-4.048 10.759,-1.402 19.561,-9.025 22.426,-19.42 4.027,-14.616 9.874,-28.725 17.377,-41.934 5.332,-9.389 4.502,-21.011 -2.113,-29.609 -5.965,-7.756 -11.888,-15.604 -17.604,-23.33 -0.84,-1.137 -0.701,-2.769 0.324,-3.793 l 32.365,-32.367 c 1.024,-1.026 2.655,-1.163 3.792,-0.324 7.697,5.694 15.547,11.617 23.33,17.605 8.6,6.614 20.221,7.445 29.611,2.112 13.203,-7.5 27.312,-13.347 41.932,-17.377 10.395,-2.865 18.019,-11.667 19.422,-22.426 1.27,-9.731 2.631,-19.465 4.048,-28.933 0.209,-1.397 1.461,-2.452 2.911,-2.452 h 45.773 c 1.45,0 2.702,1.054 2.911,2.453 1.417,9.465 2.778,19.198 4.048,28.932 1.403,10.759 9.027,19.561 19.421,22.426 14.62,4.03 28.728,9.877 41.934,17.377 9.388,5.33 21.01,4.502 29.608,-2.114 7.783,-5.987 15.633,-11.91 23.329,-17.604 1.137,-0.842 2.769,-0.703 3.794,0.324 l 32.366,32.366 c 1.026,1.026 1.164,2.657 0.324,3.793 -5.705,7.714 -11.628,15.562 -17.604,23.33 -6.615,8.601 -7.445,20.223 -2.112,29.612 7.501,13.205 13.347,27.313 17.377,41.933 2.865,10.394 11.669,18.016 22.424,19.418 9.716,1.268 19.451,2.63 28.934,4.048 1.399,0.21 2.453,1.461 2.453,2.911 v 45.773 z"
+ id="path2"
+ inkscape:connector-curvature="0" />
+ </g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g12">
+ <g
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g10">
+ <path
+ inkscape:connector-curvature="0"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path8"
+ d="m 256,144.866 c -61.28,0 -111.134,49.854 -111.134,111.134 0,61.28 49.854,111.134 111.134,111.134 61.28,0 111.134,-49.854 111.134,-111.134 0,-61.28 -49.854,-111.134 -111.134,-111.134 z m 0,198.359 c -48.097,0 -87.225,-39.129 -87.225,-87.225 0,-48.097 39.13,-87.225 87.225,-87.225 48.096,0 87.225,39.129 87.225,87.225 0,48.096 -39.128,87.225 -87.225,87.225 z" />
+ </g>
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g14">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g16">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g18">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g20">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g22">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g24">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g26">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g28">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g30">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g32">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g34">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g36">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g38">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g40">
+</g><g
+ transform="translate(5,5)"
+ style="stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="g42">
+</g></g>
+</svg> \ No newline at end of file