This commit is contained in:
csf
2022-06-23 17:42:30 +08:00
parent c1906914a0
commit ece86cda9e
13 changed files with 493 additions and 206 deletions

View File

@@ -159,6 +159,7 @@ class Header: Reactor.Component {
<li #custom type="image-quality"><span>{svg_checkmark}</span>{translate('Custom')}</li>
<div .separator />
<li #show-remote-cursor .toggle-option><span>{svg_checkmark}</span>{translate('Show remote cursor')}</li>
<li #show-quality-monitor .toggle-option><span>{svg_checkmark}</span>{translate('Show quality monitor')}</li>
{audio_enabled ? <li #disable-audio .toggle-option><span>{svg_checkmark}</span>{translate('Mute')}</li> : ""}
{is_win && pi.platform == 'Windows' && file_enabled ? <li #enable-file-transfer .toggle-option><span>{svg_checkmark}</span>{translate('Allow file copy and paste')}</li> : ""}
{keyboard_enabled && clipboard_enabled ? <li #disable-clipboard .toggle-option><span>{svg_checkmark}</span>{translate('Disable clipboard')}</li> : ""}
@@ -315,7 +316,9 @@ class Header: Reactor.Component {
handle_custom_image_quality();
} else if (me.id == "privacy-mode") {
togglePrivacyMode(me.id);
} else if (me.attributes.hasClass("toggle-option")) {
} else if (me.id == "show-quality-monitor") {
toggleQualityMonitor(me.id);
}else if (me.attributes.hasClass("toggle-option")) {
handler.toggle_option(me.id);
toggleMenuState();
} else if (!me.attributes.hasClass("selected")) {
@@ -332,16 +335,13 @@ class Header: Reactor.Component {
}
function handle_custom_image_quality() {
var tmp = handler.get_custom_image_quality();
var bitrate0 = tmp[0] || 50;
var quantizer0 = tmp.length > 1 ? tmp[1] : 100;
var bitrate = handler.get_custom_image_quality()[0] / 2;
msgbox("custom", "Custom Image Quality", "<div .form> \
<div><input type=\"hslider\" style=\"width: 50%\" name=\"bitrate\" max=\"100\" min=\"10\" value=\"" + bitrate0 + "\"/ buddy=\"bitrate-buddy\"><b #bitrate-buddy>x</b>% bitrate</div> \
<div><input type=\"hslider\" style=\"width: 50%\" name=\"quantizer\" max=\"100\" min=\"0\" value=\"" + quantizer0 + "\"/ buddy=\"quantizer-buddy\"><b #quantizer-buddy>x</b>% quantizer</div> \
<div><input type=\"hslider\" style=\"width: 50%\" name=\"bitrate\" max=\"100\" min=\"10\" value=\"" + bitrate + "\"/ buddy=\"bitrate-buddy\"><b #bitrate-buddy>x</b>% Bitrate</div> \
</div>", function(res=null) {
if (!res) return;
if (!res.bitrate) return;
handler.save_custom_image_quality(res.bitrate, res.quantizer);
handler.save_custom_image_quality(res.bitrate * 2);
toggleMenuState();
});
}
@@ -357,7 +357,7 @@ function toggleMenuState() {
for (var el in $$(menu#display-options>li)) {
el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0);
}
for (var id in ["show-remote-cursor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end"]) {
for (var id in ["show-remote-cursor", "show-quality-monitor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end"]) {
var el = self.select('#' + id);
if (el) {
var value = handler.get_toggle_option(id);
@@ -425,6 +425,17 @@ function togglePrivacyMode(privacy_id) {
}
}
function toggleQualityMonitor(name) {
var show = handler.get_toggle_option(name);
if (show) {
$(#quality-monitor).style.set{ display: "none" };
} else {
$(#quality-monitor).style.set{ display: "block" };
}
handler.toggle_option(name);
toggleMenuState();
}
handler.updateBlockInputState = function(input_blocked) {
if (!input_blocked) {
handler.toggle_option("block-input");

View File

@@ -9,6 +9,16 @@ div#video-wrapper {
background: #212121;
}
div#quality-monitor {
top: 20px;
right: 20px;
background: #7571719c;
padding: 5px;
min-width: 150px;
color: azure;
border: solid azure;
}
video#handler {
behavior: native-remote video;
size: *;
@@ -24,7 +34,7 @@ img#cursor {
}
.goup {
transform: rotate(90deg);
transform: rotate(90deg);
}
table#remote-folder-view {
@@ -33,4 +43,4 @@ table#remote-folder-view {
table#local-folder-view {
context-menu: selector(menu#local-folder-view);
}
}

View File

@@ -1,12 +1,13 @@
<html window-resizable window-frame="extended">
<head>
<style>
@import url(common.css);
@import url(remote.css);
@import url(file_transfer.css);
@import url(header.css);
</style>
<script type="text/tiscript">
<head>
<style>
@import url(common.css);
@import url(remote.css);
@import url(file_transfer.css);
@import url(header.css);
</style>
<script type="text/tiscript">
include "common.tis";
include "msgbox.tis";
include "remote.tis";
@@ -15,23 +16,28 @@
include "grid.tis";
include "header.tis";
</script>
</head>
<header>
<div.window-icon role="window-icon"><icon /></div>
</head>
<header>
<div.window-icon role="window-icon">
<icon />
</div>
<caption role="window-caption" />
<div.window-toolbar />
<div.window-buttons />
</header>
<body>
<div #video-wrapper>
<video #handler>
<div style="position: relative">
<img #cursor src="in-memory:cursor" />
</div>
</video>
</div>
<div #file-transfer-wrapper>
</div>
<div #msgbox />
</body>
</html>
</header>
<body>
<div #video-wrapper>
<video #handler>
<div #quality-monitor style="position: absolute; display: none" />
<div style="position: relative">
<img #cursor src="in-memory:cursor" />
</div>
</video>
</div>
<div #file-transfer-wrapper>
</div>
<div #msgbox />
</body>
</html>

View File

@@ -2,7 +2,7 @@ use std::{
collections::HashMap,
ops::Deref,
sync::{
atomic::{AtomicBool, Ordering},
atomic::{AtomicBool, AtomicUsize, Ordering},
Arc, Mutex, RwLock,
},
};
@@ -223,7 +223,7 @@ impl sciter::EventHandler for Handler {
fn get_custom_image_quality();
fn save_view_style(String);
fn save_image_quality(String);
fn save_custom_image_quality(i32, i32);
fn save_custom_image_quality(i32);
fn refresh_video();
fn get_toggle_option(String);
fn is_privacy_mode_supported();
@@ -234,6 +234,25 @@ impl sciter::EventHandler for Handler {
}
}
#[derive(Debug)]
struct QualityStatus {
speed: String,
fps: i32,
delay: i32,
target_bitrate: i32,
}
impl Default for QualityStatus {
fn default() -> Self {
Self {
speed: Default::default(),
fps: -1,
delay: -1,
target_bitrate: -1,
}
}
}
impl Handler {
pub fn new(cmd: String, id: String, args: Vec<String>) -> Self {
let me = Self {
@@ -249,6 +268,18 @@ impl Handler {
me
}
fn update_quality_status(&self, status: QualityStatus) {
self.call2(
"updateQualityStatus",
&make_args!(
status.speed,
status.fps,
status.delay,
status.target_bitrate
),
);
}
fn start_keyboard_hook(&self) {
if self.is_port_forward() || self.is_file_transfer() {
return;
@@ -533,12 +564,12 @@ impl Handler {
self.send(Data::Message(LoginConfigHandler::refresh()));
}
fn save_custom_image_quality(&mut self, bitrate: i32, quantizer: i32) {
fn save_custom_image_quality(&mut self, custom_image_quality: i32) {
let msg = self
.lc
.write()
.unwrap()
.save_custom_image_quality(bitrate, quantizer);
.save_custom_image_quality(custom_image_quality as u32);
self.send(Data::Message(msg));
}
@@ -1296,7 +1327,10 @@ async fn io_loop(handler: Handler) {
}
return;
}
let (video_sender, audio_sender) = start_video_audio_threads(|data: &[u8]| {
let frame_count = Arc::new(AtomicUsize::new(0));
let frame_count_cl = frame_count.clone();
let (video_sender, audio_sender) = start_video_audio_threads(move |data: &[u8]| {
frame_count_cl.fetch_add(1, Ordering::Relaxed);
VIDEO
.lock()
.unwrap()
@@ -1319,6 +1353,8 @@ async fn io_loop(handler: Handler) {
first_frame: false,
#[cfg(windows)]
clipboard_file_context: None,
data_count: Arc::new(AtomicUsize::new(0)),
frame_count,
};
remote.io_loop(&key, &token).await;
remote.sync_jobs_status_to_local().await;
@@ -1369,6 +1405,8 @@ struct Remote {
first_frame: bool,
#[cfg(windows)]
clipboard_file_context: Option<Box<CliprdrClientContext>>,
data_count: Arc<AtomicUsize>,
frame_count: Arc<AtomicUsize>,
}
impl Remote {
@@ -1394,6 +1432,8 @@ impl Remote {
#[cfg(windows)]
let mut rx_clip_client = get_rx_clip_client().lock().await;
let mut status_timer = time::interval(Duration::new(1, 0));
loop {
tokio::select! {
res = peer.next() => {
@@ -1406,6 +1446,7 @@ impl Remote {
}
Ok(ref bytes) => {
last_recv_time = Instant::now();
self.data_count.fetch_add(bytes.len(), Ordering::Relaxed);
if !self.handle_msg_from_peer(bytes, &mut peer).await {
break
}
@@ -1450,6 +1491,16 @@ impl Remote {
self.timer = time::interval_at(Instant::now() + SEC30, SEC30);
}
}
_ = status_timer.tick() => {
let speed = self.data_count.swap(0, Ordering::Relaxed);
let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32);
let fps = self.frame_count.swap(0, Ordering::Relaxed) as _;
self.handler.update_quality_status(QualityStatus {
speed,
fps,
..Default::default()
});
}
}
}
log::debug!("Exit io_loop of id={}", self.handler.id);
@@ -2370,7 +2421,7 @@ impl Remote {
}
back_notification::PrivacyModeState::OffSucceeded => {
self.handler
.msgbox("custom-nocancel", "Privacy mode", "Out privacy mode");
.msgbox("custom-nocancel", "Privacy mode", "Out privacy mode");
self.update_privacy_mode(false);
}
back_notification::PrivacyModeState::OffByPeer => {
@@ -2549,7 +2600,16 @@ impl Interface for Handler {
}
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {
handle_test_delay(t, peer).await;
if !t.from_client {
self.update_quality_status(QualityStatus {
delay: t.last_delay as _,
target_bitrate: t.target_bitrate as _,
..Default::default()
});
let mut msg_out = Message::new();
msg_out.set_test_delay(t);
allow_err!(peer.send(&msg_out).await);
}
}
}

View File

@@ -456,6 +456,45 @@ function self.closing() {
if (is_file_transfer || is_port_forward || size_adapted) handler.save_size(x, y, w, h);
}
var qualityMonitor;
var qualityMonitorData = [];
class QualityMonitor: Reactor.Component
{
function this() {
qualityMonitor = this;
if (handler.get_toggle_option("show-quality-monitor")) {
$(#quality-monitor).style.set{ display: "block" };
}
}
function render() {
return <div >
<div>
Speed: {qualityMonitorData[0]}
</div>
<div>
FPS: {qualityMonitorData[1]}
</div>
<div>
Delay: {qualityMonitorData[2]} ms
</div>
<div>
Target Bitrate: {qualityMonitorData[3]}kb
</div>
</div>;
}
}
$(#quality-monitor).content(<QualityMonitor />);
handler.updateQualityStatus = function(speed, fps, delay, bitrate) {
speed ? qualityMonitorData[0] = speed:null;
fps > -1 ? qualityMonitorData[1] = fps:null;
delay > -1 ? qualityMonitorData[2] = delay:null;
bitrate > -1 ? qualityMonitorData[3] = bitrate:null;
qualityMonitor.update();
}
handler.setPermission = function(name, enabled) {
self.timer(60ms, function() {
if (name == "keyboard") keyboard_enabled = enabled;