better ui

This commit is contained in:
Devaev Maxim
2018-07-13 20:43:45 +00:00
parent e118d270df
commit 73ec9d853e
5 changed files with 117 additions and 65 deletions

View File

@@ -56,6 +56,14 @@ class Hid(multiprocessing.Process):
# TODO: add reset or power switching # TODO: add reset or power switching
def get_state(self) -> Dict:
return {
"features": {
"keyboard": True, # Always
"mouse": False, # TODO
},
}
async def send_key_event(self, key: str, state: bool) -> None: async def send_key_event(self, key: str, state: bool) -> None:
if not self.__stop_event.is_set(): if not self.__stop_event.is_set():
async with self.__lock: async with self.__lock:

View File

@@ -121,6 +121,8 @@ class Server: # pylint: disable=too-many-instance-attributes
app.router.add_get("/ws", self.__ws_handler) app.router.add_get("/ws", self.__ws_handler)
app.router.add_get("/hid", self.__hid_state_handler)
app.router.add_get("/atx", self.__atx_state_handler) app.router.add_get("/atx", self.__atx_state_handler)
app.router.add_post("/atx/click", self.__atx_click_handler) app.router.add_post("/atx/click", self.__atx_click_handler)
@@ -170,6 +172,9 @@ class Server: # pylint: disable=too-many-instance-attributes
break break
return ws return ws
async def __hid_state_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response:
return _json(self.__hid.get_state())
async def __atx_state_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response: async def __atx_state_handler(self, _: aiohttp.web.Request) -> aiohttp.web.Response:
return _json(self.__atx.get_state()) return _json(self.__atx.get_state())

View File

@@ -1,40 +1,48 @@
.stream-box { div#stream-box {
box-sizing: border-box; box-sizing: border-box;
display: inline-block; display: inline-block;
background-color: #e5e5f5; background-color: #e5e5f5;
padding: 10px; padding: 10px;
} }
.stream-image { img#stream-image {
width: 640px; width: 640px;
height: 480px; height: 480px;
display: inline-block;
border: 1px solid grey; border: 1px solid grey;
}
img.stream-image-on {
cursor: cell;
-webkit-filter: none;
filter: none;
}
img.stream-image-off {
cursor: wait; cursor: wait;
-webkit-filter: grayscale(100%); /* Safari 6.0 - 9.0 */
filter: grayscale(100%);
} }
.session-opened { div.session-active {
color: grey; color: grey;
} }
.session-closed { div.session-closed {
color: #ff3d40; color: #ff3d40;
} }
div#power-led, div#hdd-led { div#atx-power-led, div#hid-mouse-led, div#msd-led {
border-radius: 50%; font-weight: bold;
border: 1px solid grey; font-size: 150%;
width: 16px;
height: 16px;
} }
div.power-led-on { div#atx-hdd-led, div#screen-led, div#hid-keyboard-led {
background: #00ce00; font-size: 150%;
} }
div.power-led-off { div.led-on {
background: #adceab; color: #00ce00;
} }
div.hdd-led-on { div.led-off {
background: #ff373a; color: grey;
} }
div.hdd-led-off { div.led-busy {
background: #ffcdce; color: #ff373a;
} }
button#power-button, button#reset-button { button#power-button, button#reset-button {

View File

@@ -7,19 +7,21 @@
</head> </head>
<script src="js/kvmd.js"></script> <script src="js/kvmd.js"></script>
<script> <script>window.onload = runKvmdUi;</script>
window.onload = function() {
runKvmdSession();
pollStreamer();
}
</script>
<body> <body>
<div id="stream-box" class="stream-box"> <div id="stream-box">
<img src="/streamer/?action=stream" id="stream-image" class="stream-image" alt="" /> <img src="/streamer/?action=stream" id="stream-image" class="stream-image-off" alt="Loading..."/>
<hr>
<table cellpadding="0" cellspacing="0" style="border: none;"> <table cellpadding="0" cellspacing="0" style="border: none;">
<tr> <tr>
<td><button id="reset-streamer-button" type="button" title="Click here if your video looks like crap" onclick="clickResetStreamerButton(this);">Reset stream</button></td> <td><div id="screen-led" class="led-off">&#128437;</div></td>
<td>&nbsp;</td>
<td><div id="hid-keyboard-led" class="led-off">&#9000;</div></td>
<td>&nbsp;</td>
<td><div id="hid-mouse-led" class="led-off">&#128432;</div></td>
<td>&nbsp;&nbsp;</td>
<td><button id="reset-stream-button" type="button" title="Click here if your video looks like crap" onclick="clickResetStreamButton(this);">Reset stream</button></td>
<td>&nbsp;&nbsp;</td> <td>&nbsp;&nbsp;</td>
<td><div id="session-status">Not connected yet...</div></td> <td><div id="session-status">Not connected yet...</div></td>
</tr> </tr>
@@ -28,9 +30,11 @@
<table cellpadding="0" cellspacing="5" style="border: none;"> <table cellpadding="0" cellspacing="5" style="border: none;">
<tr> <tr>
<td><div id="power-led" class="power-led-off"></div></td> <td><div id="atx-power-led" class="led-off">&#x23FB;</div></td>
<td><div id="hdd-led" class="hdd-led-off"></div></td> <td><div id="atx-hdd-led" class="led-off">&#128436;</div></td>
<td>&nbsp;&nbsp;</td> <td>&nbsp;</td>
<!--<td><div id="msd-led" class="led-off">&#9991;</div></td>
<td>&nbsp;</td>-->
<td><button id="atx-power-button" type="button" title="Click hardware power button" onclick="clickAtxButton(this);">Power</button></td> <td><button id="atx-power-button" type="button" title="Click hardware power button" onclick="clickAtxButton(this);">Power</button></td>
<td><button id="atx-power-button-long" type="button" title="Click hardware power button (long press)" onclick="clickAtxButton(this);">Power (long)</button></td> <td><button id="atx-power-button-long" type="button" title="Click hardware power button (long press)" onclick="clickAtxButton(this);">Power (long)</button></td>
<td><button id="atx-reset-button" type="button" title="Click to force reset" onclick="clickAtxButton(this);">Reset</button></td> <td><button id="atx-reset-button" type="button" title="Click to force reset" onclick="clickAtxButton(this);">Reset</button></td>

View File

@@ -1,9 +1,16 @@
function runKvmdSession() { function runKvmdUi() {
__startSessionPoller();
__startStreamPoller();
}
// -----------------------------------------------------------------------------
function __startSessionPoller() {
var ws = new WebSocket("ws://" + location.host + "/kvmd/ws"); var ws = new WebSocket("ws://" + location.host + "/kvmd/ws");
ws.onopen = function(event) { ws.onopen = function(event) {
__installHidHandlers(ws); __installHidHandlers(ws);
__setSessionStatus("session-opened", "Session opened (keyboard captured)"); __setSessionStatus(true);
}; };
ws.onmessage = function(event) { ws.onmessage = function(event) {
@@ -12,18 +19,18 @@ function runKvmdSession() {
if (event.msg_type == "event") { if (event.msg_type == "event") {
if (event.msg.event == "atx_state") { if (event.msg.event == "atx_state") {
leds = event.msg.event_attrs.leds; leds = event.msg.event_attrs.leds;
document.getElementById("power-led").className = "power-led-" + (leds.power ? "on" : "off"); document.getElementById("atx-power-led").className = (leds.power ? "led-on" : "led-off");
document.getElementById("hdd-led").className = "hdd-led-" + (leds.hdd ? "on" : "off"); document.getElementById("atx-hdd-led").className = (leds.hdd ? "led-busy" : "led-off");
} }
} }
}; };
ws.onclose = function(event) { ws.onclose = function(event) {
__clearHidHandlers(); __clearHidHandlers();
__setSessionStatus("session-closed", "Session closed (keyboard free), trying to reconnect..."); __setSessionStatus(false);
document.getElementById("power-led").className = "power-led-off"; document.getElementById("atx-power-led").className = "led-off";
document.getElementById("hdd-led").className = "hdd-led-off"; document.getElementById("atx-hdd-led").className = "led-off";
setTimeout(runKvmdSession, 5000); setTimeout(__startSessionPoller, 2000);
}; };
ws.onerror = function(error) { ws.onerror = function(error) {
@@ -31,29 +38,47 @@ function runKvmdSession() {
}; };
} }
function __setSessionStatus(cls, msg) { function __setSessionStatus(status) {
var el_session_status = document.getElementById("session-status"); var el_session_status = document.getElementById("session-status");
el_session_status.innerHTML = msg; el_session_status.innerHTML = (status ? "Session active" : "Session closed, trying to reconnect...");
el_session_status.className = cls; el_session_status.className = (status ? "session-active" : "session-closed");
} }
function __installHidHandlers(ws) { function __installHidHandlers(ws) {
// https://www.codeday.top/2017/05/03/24906.html var http = __request("GET", "/kvmd/hid", function() {
document.onkeydown = (event) => __onKeyEvent(ws, event, true); if (http.readyState == 4) {
document.onkeyup = (event) => __onKeyEvent(ws, event, false); if (http.status == 200) {
features = JSON.parse(http.responseText).result.features;
if (features.keyboard) {
// https://www.codeday.top/2017/05/03/24906.html
document.onkeydown = (event) => __onKeyEvent(ws, event, true);
document.onkeyup = (event) => __onKeyEvent(ws, event, false);
document.getElementById("hid-keyboard-led").className = "led-on";
}
if (features.mouse) {
el_stream_image = document.getElementById("stream-image");
el_stream_image.onmousedown = (event) => __onMouseButton(ws, event, true);
el_stream_image.onmouseup = (event) => __onMouseButton(ws, event, false);
el_stream_image.oncontextmenu = (event) => event.preventDefault();
el_stream_image.onmousemove = __onMouseMove;
el_stream_image.onwheel = (event) => __onMouseWheel(ws, event);
document.getElementById("hid-mouse-led").className = "led-on";
el_stream_image = document.getElementById("stream-image"); __installHidHandlers.mouse_move_timer = setInterval(() => __handleMouseMove(ws), 100);
el_stream_image.onmousedown = (event) => __onMouseButton(ws, event, true); }
el_stream_image.onmouseup = (event) => __onMouseButton(ws, event, false); } else {
el_stream_image.oncontextmenu = (event) => event.preventDefault(); alert("Can't fetch HID features:", http.responseText);
el_stream_image.onmousemove = __onMouseMove; }
el_stream_image.onwheel = (event) => __onMouseWheel(ws, event); }
runKvmdSession.mouse_move_timer = setInterval(() => __handleMouseMove(ws), 100); });
} }
function __clearHidHandlers() { function __clearHidHandlers() {
clearInterval(__installHidHandlers.mouse_move_timer);
document.onkeydown = null; document.onkeydown = null;
document.onkeyup = null; document.onkeyup = null;
document.getElementById("hid-keyboard-led").className = "led-off";
el_stream_image = document.getElementById("stream-image"); el_stream_image = document.getElementById("stream-image");
el_stream_image.onmousedown = null; el_stream_image.onmousedown = null;
@@ -61,7 +86,7 @@ function __clearHidHandlers() {
el_stream_image.oncontextmenu = null; el_stream_image.oncontextmenu = null;
el_stream_image.onmousemove = null; el_stream_image.onmousemove = null;
el_stream_image.onwheel = null; el_stream_image.onwheel = null;
clearInterval(runKvmdSession.mouse_move_timer); document.getElementById("hid-mouse-led").className = "led-off";
} }
function __onKeyEvent(ws, event, state) { function __onKeyEvent(ws, event, state) {
@@ -159,7 +184,7 @@ function clickAtxButton(el_button) {
if (http.status == 409) { if (http.status == 409) {
alert("Performing another ATX operation for other client, please try again later"); alert("Performing another ATX operation for other client, please try again later");
} else if (http.status != 200) { } else if (http.status != 200) {
alert("Click error: " + http.responseText); alert("Click error:", http.responseText);
} }
__setAtxButtonsBusy(false); __setAtxButtonsBusy(false);
} }
@@ -179,28 +204,30 @@ function __setAtxButtonsBusy(busy) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function pollStreamer() { function __startStreamPoller() {
var http = __request("GET", "/streamer/?action=snapshot", function() { var http = __request("GET", "/streamer/?action=snapshot", function() {
if (http.readyState == 2 || http.readyState == 4) { if (http.readyState == 2 || http.readyState == 4) {
var status = http.status; var status = http.status;
http.onreadystatechange = null; http.onreadystatechange = null;
http.abort(); http.abort();
if (status != 200) { if (status != 200) {
console.log("Refreshing streamer ..."); console.log("Refreshing stream ...");
pollStreamer.last = false; __startStreamPoller.last = false;
document.getElementById("stream-image").style.cursor = "wait"; document.getElementById("stream-image").className = "stream-image-off";
} else if (!pollStreamer.last) { document.getElementById("screen-led").className = "led-off";
__refreshStreamer(); } else if (!__startStreamPoller.last) {
document.getElementById("stream-image").style.cursor = "cell"; __refreshStream();
pollStreamer.last = true; __startStreamPoller.last = true;
document.getElementById("stream-image").className = "stream-image-on";
document.getElementById("screen-led").className = "led-on";
} }
} }
}); });
setTimeout(pollStreamer, 2000); setTimeout(__startStreamPoller, 2000);
} }
pollStreamer.last = false; __startStreamPoller.last = false;
function __refreshStreamer() { function __refreshStream() {
var http = __request("GET", "/kvmd/streamer", function() { var http = __request("GET", "/kvmd/streamer", function() {
if (http.readyState == 4 && http.status == 200) { if (http.readyState == 4 && http.status == 200) {
size = JSON.parse(http.responseText).result.size; size = JSON.parse(http.responseText).result.size;
@@ -212,12 +239,12 @@ function __refreshStreamer() {
}); });
} }
function clickResetStreamerButton(el_button) { function clickResetStreamButton(el_button) {
__setButtonBusy(el_button, true); __setButtonBusy(el_button, true);
var http = __request("POST", "/kvmd/streamer/reset", function() { var http = __request("POST", "/kvmd/streamer/reset", function() {
if (http.readyState == 4) { if (http.readyState == 4) {
if (http.status != 200) { if (http.status != 200) {
alert("Can't reset streamer: " + http.responseText); alert("Can't reset stream:", http.responseText);
} }
__setButtonBusy(el_button, false); __setButtonBusy(el_button, false);
} }