mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 00:51:53 +08:00
better ui
This commit is contained in:
@@ -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:
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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">🖵</div></td>
|
||||||
|
<td> </td>
|
||||||
|
<td><div id="hid-keyboard-led" class="led-off">⌨</div></td>
|
||||||
|
<td> </td>
|
||||||
|
<td><div id="hid-mouse-led" class="led-off">🖰</div></td>
|
||||||
|
<td> </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> </td>
|
<td> </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">⏻</div></td>
|
||||||
<td><div id="hdd-led" class="hdd-led-off"></div></td>
|
<td><div id="atx-hdd-led" class="led-off">🖴</div></td>
|
||||||
<td> </td>
|
<td> </td>
|
||||||
|
<!--<td><div id="msd-led" class="led-off">✇</div></td>
|
||||||
|
<td> </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>
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
var http = __request("GET", "/kvmd/hid", function() {
|
||||||
|
if (http.readyState == 4) {
|
||||||
|
if (http.status == 200) {
|
||||||
|
features = JSON.parse(http.responseText).result.features;
|
||||||
|
if (features.keyboard) {
|
||||||
// https://www.codeday.top/2017/05/03/24906.html
|
// https://www.codeday.top/2017/05/03/24906.html
|
||||||
document.onkeydown = (event) => __onKeyEvent(ws, event, true);
|
document.onkeydown = (event) => __onKeyEvent(ws, event, true);
|
||||||
document.onkeyup = (event) => __onKeyEvent(ws, event, false);
|
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 = document.getElementById("stream-image");
|
||||||
el_stream_image.onmousedown = (event) => __onMouseButton(ws, event, true);
|
el_stream_image.onmousedown = (event) => __onMouseButton(ws, event, true);
|
||||||
el_stream_image.onmouseup = (event) => __onMouseButton(ws, event, false);
|
el_stream_image.onmouseup = (event) => __onMouseButton(ws, event, false);
|
||||||
el_stream_image.oncontextmenu = (event) => event.preventDefault();
|
el_stream_image.oncontextmenu = (event) => event.preventDefault();
|
||||||
el_stream_image.onmousemove = __onMouseMove;
|
el_stream_image.onmousemove = __onMouseMove;
|
||||||
el_stream_image.onwheel = (event) => __onMouseWheel(ws, event);
|
el_stream_image.onwheel = (event) => __onMouseWheel(ws, event);
|
||||||
runKvmdSession.mouse_move_timer = setInterval(() => __handleMouseMove(ws), 100);
|
document.getElementById("hid-mouse-led").className = "led-on";
|
||||||
|
|
||||||
|
__installHidHandlers.mouse_move_timer = setInterval(() => __handleMouseMove(ws), 100);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert("Can't fetch HID features:", http.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user