Merge branch 'master' into keyboard
This commit is contained in:
commit
1424cbeb44
26
Cargo.lock
generated
26
Cargo.lock
generated
@ -1533,7 +1533,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"objc",
|
"objc",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"rdev",
|
"rdev 0.5.0-2 (git+https://github.com/asur4s/rdev)",
|
||||||
"serde 1.0.147",
|
"serde 1.0.147",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"tfc",
|
"tfc",
|
||||||
@ -4248,6 +4248,28 @@ dependencies = [
|
|||||||
"x11 2.20.0",
|
"x11 2.20.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rdev"
|
||||||
|
version = "0.5.0-2"
|
||||||
|
source = "git+https://github.com/rustdesk/rdev#25c29f61bfdf5d8ec50f0a8a7743bc1d85eb2c04"
|
||||||
|
dependencies = [
|
||||||
|
"cocoa",
|
||||||
|
"core-foundation 0.9.3",
|
||||||
|
"core-foundation-sys 0.8.3",
|
||||||
|
"core-graphics 0.22.3",
|
||||||
|
"enum-map",
|
||||||
|
"epoll",
|
||||||
|
"inotify",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"mio 0.8.5",
|
||||||
|
"strum 0.24.1",
|
||||||
|
"strum_macros 0.24.3",
|
||||||
|
"widestring 1.0.2",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
"x11 2.20.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rdrand"
|
name = "rdrand"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -4518,7 +4540,7 @@ dependencies = [
|
|||||||
"num_cpus",
|
"num_cpus",
|
||||||
"objc",
|
"objc",
|
||||||
"parity-tokio-ipc",
|
"parity-tokio-ipc",
|
||||||
"rdev",
|
"rdev 0.5.0-2 (git+https://github.com/rustdesk/rdev)",
|
||||||
"repng",
|
"repng",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rpassword 7.1.0",
|
"rpassword 7.1.0",
|
||||||
|
@ -63,7 +63,7 @@ default-net = "0.11.0"
|
|||||||
wol-rs = "0.9.1"
|
wol-rs = "0.9.1"
|
||||||
flutter_rust_bridge = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge", optional = true }
|
flutter_rust_bridge = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge", optional = true }
|
||||||
errno = "0.2.8"
|
errno = "0.2.8"
|
||||||
rdev = { git = "https://github.com/asur4s/rdev" }
|
rdev = { git = "https://github.com/rustdesk/rdev" }
|
||||||
url = { version = "2.1", features = ["serde"] }
|
url = { version = "2.1", features = ["serde"] }
|
||||||
|
|
||||||
reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false }
|
reqwest = { version = "0.11", features = ["blocking", "json", "rustls-tls"], default-features=false }
|
||||||
|
@ -1013,7 +1013,7 @@ class LastWindowPosition {
|
|||||||
return LastWindowPosition(m["width"], m["height"], m["offsetWidth"],
|
return LastWindowPosition(m["width"], m["height"], m["offsetWidth"],
|
||||||
m["offsetHeight"], m["isMaximized"]);
|
m["offsetHeight"], m["isMaximized"]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint(e.toString());
|
debugPrintStack(label: e.toString());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1147,7 +1147,7 @@ Future<bool> restoreWindowPosition(WindowType type, {int? windowId}) async {
|
|||||||
final pos = bind.getLocalFlutterConfig(k: kWindowPrefix + type.name);
|
final pos = bind.getLocalFlutterConfig(k: kWindowPrefix + type.name);
|
||||||
var lpos = LastWindowPosition.loadFromString(pos);
|
var lpos = LastWindowPosition.loadFromString(pos);
|
||||||
if (lpos == null) {
|
if (lpos == null) {
|
||||||
debugPrint("window position saved, but cannot be parsed");
|
debugPrint("no window position saved, ignoring position restoration");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1212,7 +1212,7 @@ Future<void> initUniLinks() async {
|
|||||||
}
|
}
|
||||||
parseRustdeskUri(initialLink);
|
parseRustdeskUri(initialLink);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debugPrint("$err");
|
debugPrintStack(label: "$err");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1422,7 +1422,7 @@ void onActiveWindowChanged() async {
|
|||||||
rustDeskWinManager.closeAllSubWindows()
|
rustDeskWinManager.closeAllSubWindows()
|
||||||
]);
|
]);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debugPrint("$err");
|
debugPrintStack(label: "$err");
|
||||||
} finally {
|
} finally {
|
||||||
await windowManager.setPreventClose(false);
|
await windowManager.setPreventClose(false);
|
||||||
await windowManager.close();
|
await windowManager.close();
|
||||||
|
@ -79,16 +79,19 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
.toList()
|
.toList()
|
||||||
.obs;
|
.obs;
|
||||||
try {
|
try {
|
||||||
final json = jsonDecode(bind.getLocalFlutterConfig(k: 'peer-tab-order'));
|
final conf = bind.getLocalFlutterConfig(k: 'peer-tab-order');
|
||||||
if (json is List) {
|
if (conf.isNotEmpty) {
|
||||||
final List<String> list = json.map((e) => e.toString()).toList();
|
final json = jsonDecode(conf);
|
||||||
if (list.length == visibleOrderedTabs.length &&
|
if (json is List) {
|
||||||
visibleOrderedTabs.every((e) => list.contains(e))) {
|
final List<String> list = json.map((e) => e.toString()).toList();
|
||||||
visibleOrderedTabs.value = list;
|
if (list.length == visibleOrderedTabs.length &&
|
||||||
|
visibleOrderedTabs.every((e) => list.contains(e))) {
|
||||||
|
visibleOrderedTabs.value = list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('$e');
|
debugPrintStack(label: '$e');
|
||||||
}
|
}
|
||||||
|
|
||||||
adjustTab();
|
adjustTab();
|
||||||
|
@ -63,7 +63,7 @@ class DesktopSettingPage extends StatefulWidget {
|
|||||||
DesktopTabPage.onAddSetting(initialPage: page);
|
DesktopTabPage.onAddSetting(initialPage: page);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('$e');
|
debugPrintStack(label: '$e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ class DesktopTabPage extends StatefulWidget {
|
|||||||
initialPage: initialPage,
|
initialPage: initialPage,
|
||||||
)));
|
)));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('$e');
|
debugPrintStack(label: '$e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -460,7 +460,7 @@ Future<bool> loginDialog() async {
|
|||||||
debugPrint('$resp');
|
debugPrint('$resp');
|
||||||
completer.complete(true);
|
completer.complete(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debugPrint(err.toString());
|
debugPrintStack(label: err.toString());
|
||||||
cancel();
|
cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -564,7 +564,7 @@ void androidChannelInit() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("MethodCallHandler err:$e");
|
debugPrintStack(label: "MethodCallHandler err:$e");
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
|
@ -363,8 +363,6 @@ class FileModel extends ChangeNotifier {
|
|||||||
if (_currentRemoteDir.path.isEmpty) {
|
if (_currentRemoteDir.path.isEmpty) {
|
||||||
openDirectory(_remoteOption.home, isLocal: false);
|
openDirectory(_remoteOption.home, isLocal: false);
|
||||||
}
|
}
|
||||||
// load last transfer jobs
|
|
||||||
await bind.sessionLoadLastTransferJobs(id: '${parent.target?.id}');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onClose() async {
|
Future<void> onClose() async {
|
||||||
|
@ -126,7 +126,7 @@ class PlatformFFI {
|
|||||||
// no need to set home dir
|
// no need to set home dir
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('initialize failed: $e');
|
debugPrintStack(label: 'initialize failed: $e');
|
||||||
}
|
}
|
||||||
String id = 'NA';
|
String id = 'NA';
|
||||||
String name = 'Flutter';
|
String name = 'Flutter';
|
||||||
@ -151,9 +151,8 @@ class PlatformFFI {
|
|||||||
WindowsDeviceInfo winInfo = await deviceInfo.windowsInfo;
|
WindowsDeviceInfo winInfo = await deviceInfo.windowsInfo;
|
||||||
name = winInfo.computerName;
|
name = winInfo.computerName;
|
||||||
id = winInfo.computerName;
|
id = winInfo.computerName;
|
||||||
} catch (e, stacktrace) {
|
} catch (e) {
|
||||||
debugPrint("get windows device info failed: $e");
|
debugPrintStack(label: "get windows device info failed: $e");
|
||||||
debugPrintStack(stackTrace: stacktrace);
|
|
||||||
name = "unknown";
|
name = "unknown";
|
||||||
id = "unknown";
|
id = "unknown";
|
||||||
}
|
}
|
||||||
@ -174,7 +173,7 @@ class PlatformFFI {
|
|||||||
await _ffiBind.mainSetHomeDir(home: _homeDir);
|
await _ffiBind.mainSetHomeDir(home: _homeDir);
|
||||||
await _ffiBind.mainInit(appDir: _dir);
|
await _ffiBind.mainInit(appDir: _dir);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('initialize failed: $e');
|
debugPrintStack(label: 'initialize failed: $e');
|
||||||
}
|
}
|
||||||
version = await getVersion();
|
version = await getVersion();
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,8 @@ pub fn get_display_server() -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_display_server_of_session(session: &str) -> String {
|
fn get_display_server_of_session(session: &str) -> String {
|
||||||
if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "Type", session]))
|
let mut display_server = if let Ok(output) =
|
||||||
|
run_loginctl(Some(vec!["show-session", "-p", "Type", session]))
|
||||||
// Check session type of the session
|
// Check session type of the session
|
||||||
{
|
{
|
||||||
let display_server = String::from_utf8_lossy(&output.stdout)
|
let display_server = String::from_utf8_lossy(&output.stdout)
|
||||||
@ -64,28 +65,23 @@ fn get_display_server_of_session(session: &str) -> String {
|
|||||||
{
|
{
|
||||||
if xorg_results.trim_end().to_string() != "" {
|
if xorg_results.trim_end().to_string() != "" {
|
||||||
// If it is, manually return "x11", otherwise return tty
|
// If it is, manually return "x11", otherwise return tty
|
||||||
"x11".to_owned()
|
return "x11".to_owned();
|
||||||
} else {
|
|
||||||
display_server
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// If any of these commands fail just fall back to the display server
|
|
||||||
display_server
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
display_server
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// loginctl has not given the expected output. try something else.
|
|
||||||
if let Ok(sestype) = std::env::var("XDG_SESSION_TYPE") {
|
|
||||||
return sestype.to_owned();
|
|
||||||
}
|
|
||||||
// If the session is not a tty, then just return the type as usual
|
|
||||||
display_server
|
|
||||||
}
|
}
|
||||||
|
display_server
|
||||||
} else {
|
} else {
|
||||||
"".to_owned()
|
"".to_owned()
|
||||||
|
};
|
||||||
|
if display_server.is_empty() {
|
||||||
|
// loginctl has not given the expected output. try something else.
|
||||||
|
if let Ok(sestype) = std::env::var("XDG_SESSION_TYPE") {
|
||||||
|
display_server = sestype;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// If the session is not a tty, then just return the type as usual
|
||||||
|
display_server
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_values_of_seat0(indices: Vec<usize>) -> Vec<String> {
|
pub fn get_values_of_seat0(indices: Vec<usize>) -> Vec<String> {
|
||||||
@ -126,8 +122,7 @@ pub fn get_values_of_seat0(indices: Vec<usize>) -> Vec<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_active(sid: &str) -> bool {
|
fn is_active(sid: &str) -> bool {
|
||||||
if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "State", sid]))
|
if let Ok(output) = run_loginctl(Some(vec!["show-session", "-p", "State", sid])) {
|
||||||
{
|
|
||||||
String::from_utf8_lossy(&output.stdout).contains("active")
|
String::from_utf8_lossy(&output.stdout).contains("active")
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -936,7 +936,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
self.handle_job_status(d.id, d.file_num, err);
|
self.handle_job_status(d.id, d.file_num, err);
|
||||||
}
|
}
|
||||||
Some(file_response::Union::Error(e)) => {
|
Some(file_response::Union::Error(e)) => {
|
||||||
if let Some(job) = fs::get_job(e.id, &mut self.write_jobs) {
|
if let Some(_job) = fs::get_job(e.id, &mut self.write_jobs) {
|
||||||
fs::remove_job(e.id, &mut self.write_jobs);
|
fs::remove_job(e.id, &mut self.write_jobs);
|
||||||
}
|
}
|
||||||
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
||||||
|
@ -78,7 +78,7 @@ pub enum FS {
|
|||||||
WriteError {
|
WriteError {
|
||||||
id: i32,
|
id: i32,
|
||||||
file_num: i32,
|
file_num: i32,
|
||||||
err: String
|
err: String,
|
||||||
},
|
},
|
||||||
WriteOffset {
|
WriteOffset {
|
||||||
id: i32,
|
id: i32,
|
||||||
@ -544,7 +544,7 @@ async fn check_pid(postfix: &str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hbb_common::allow_err!(std::fs::remove_file(&Config::ipc_path(postfix)));
|
std::fs::remove_file(&Config::ipc_path(postfix)).ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -246,16 +246,18 @@ pub async fn setup_uinput(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultT
|
|||||||
pub async fn update_mouse_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> {
|
pub async fn update_mouse_resolution(minx: i32, maxx: i32, miny: i32, maxy: i32) -> ResultType<()> {
|
||||||
set_uinput_resolution(minx, maxx, miny, maxy).await?;
|
set_uinput_resolution(minx, maxx, miny, maxy).await?;
|
||||||
|
|
||||||
if let Some(mouse) = ENIGO.lock().unwrap().get_custom_mouse() {
|
std::thread::spawn(|| {
|
||||||
if let Some(mouse) = mouse
|
if let Some(mouse) = ENIGO.lock().unwrap().get_custom_mouse() {
|
||||||
.as_mut_any()
|
if let Some(mouse) = mouse
|
||||||
.downcast_mut::<super::uinput::client::UInputMouse>()
|
.as_mut_any()
|
||||||
{
|
.downcast_mut::<super::uinput::client::UInputMouse>()
|
||||||
allow_err!(mouse.send_refresh());
|
{
|
||||||
} else {
|
allow_err!(mouse.send_refresh());
|
||||||
log::error!("failed downcast uinput mouse");
|
} else {
|
||||||
|
log::error!("failed downcast uinput mouse");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use evdev::{
|
|||||||
uinput::{VirtualDevice, VirtualDeviceBuilder},
|
uinput::{VirtualDevice, VirtualDeviceBuilder},
|
||||||
AttributeSet, EventType, InputEvent,
|
AttributeSet, EventType, InputEvent,
|
||||||
};
|
};
|
||||||
use hbb_common::{allow_err, bail, log, tokio, ResultType};
|
use hbb_common::{allow_err, bail, log, tokio::{self, runtime::Runtime}, ResultType};
|
||||||
|
|
||||||
static IPC_CONN_TIMEOUT: u64 = 1000;
|
static IPC_CONN_TIMEOUT: u64 = 1000;
|
||||||
static IPC_REQUEST_TIMEOUT: u64 = 1000;
|
static IPC_REQUEST_TIMEOUT: u64 = 1000;
|
||||||
@ -17,24 +17,24 @@ pub mod client {
|
|||||||
|
|
||||||
pub struct UInputKeyboard {
|
pub struct UInputKeyboard {
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
|
rt: Runtime,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UInputKeyboard {
|
impl UInputKeyboard {
|
||||||
pub async fn new() -> ResultType<Self> {
|
pub async fn new() -> ResultType<Self> {
|
||||||
let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_KEYBOARD).await?;
|
let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_KEYBOARD).await?;
|
||||||
Ok(Self { conn })
|
let rt = Runtime::new()?;
|
||||||
|
Ok(Self { conn, rt })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
fn send(&mut self, data: Data) -> ResultType<()> {
|
||||||
async fn send(&mut self, data: Data) -> ResultType<()> {
|
self.rt.block_on(self.conn.send(&data))
|
||||||
self.conn.send(&data).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
fn send_get_key_state(&mut self, data: Data) -> ResultType<bool> {
|
||||||
async fn send_get_key_state(&mut self, data: Data) -> ResultType<bool> {
|
self.rt.block_on(self.conn.send(&data))?;
|
||||||
self.conn.send(&data).await?;
|
|
||||||
|
|
||||||
match self.conn.next_timeout(IPC_REQUEST_TIMEOUT).await {
|
match self.rt.block_on(self.conn.next_timeout(IPC_REQUEST_TIMEOUT)) {
|
||||||
Ok(Some(Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(state)))) => {
|
Ok(Some(Data::KeyboardResponse(ipc::DataKeyboardResponse::GetKeyState(state)))) => {
|
||||||
Ok(state)
|
Ok(state)
|
||||||
}
|
}
|
||||||
@ -101,17 +101,18 @@ pub mod client {
|
|||||||
|
|
||||||
pub struct UInputMouse {
|
pub struct UInputMouse {
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
|
rt: Runtime,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UInputMouse {
|
impl UInputMouse {
|
||||||
pub async fn new() -> ResultType<Self> {
|
pub async fn new() -> ResultType<Self> {
|
||||||
let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_MOUSE).await?;
|
let conn = ipc::connect(IPC_CONN_TIMEOUT, IPC_POSTFIX_MOUSE).await?;
|
||||||
Ok(Self { conn })
|
let rt = Runtime::new()?;
|
||||||
|
Ok(Self { conn, rt })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
fn send(&mut self, data: Data) -> ResultType<()> {
|
||||||
async fn send(&mut self, data: Data) -> ResultType<()> {
|
self.rt.block_on(self.conn.send(&data))
|
||||||
self.conn.send(&data).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_refresh(&mut self) -> ResultType<()> {
|
pub fn send_refresh(&mut self) -> ResultType<()> {
|
||||||
@ -586,6 +587,16 @@ pub mod service {
|
|||||||
match data {
|
match data {
|
||||||
Data::Mouse(data) => {
|
Data::Mouse(data) => {
|
||||||
if let DataMouse::Refresh = data {
|
if let DataMouse::Refresh = data {
|
||||||
|
let resolution = RESOLUTION.lock().unwrap();
|
||||||
|
let rng_x = resolution.0.clone();
|
||||||
|
let rng_y = resolution.1.clone();
|
||||||
|
log::info!(
|
||||||
|
"Refresh uinput mouce with rng_x: ({}, {}), rng_y: ({}, {})",
|
||||||
|
rng_x.0,
|
||||||
|
rng_x.1,
|
||||||
|
rng_y.0,
|
||||||
|
rng_y.1
|
||||||
|
);
|
||||||
mouse = match mouce::Mouse::new_uinput(rng_x, rng_y) {
|
mouse = match mouce::Mouse::new_uinput(rng_x, rng_y) {
|
||||||
Ok(mouse) => mouse,
|
Ok(mouse) => mouse,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -21,8 +21,9 @@
|
|||||||
use super::{video_qos::VideoQoS, *};
|
use super::{video_qos::VideoQoS, *};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use crate::portable_service::client::PORTABLE_SERVICE_RUNNING;
|
use crate::portable_service::client::PORTABLE_SERVICE_RUNNING;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use hbb_common::get_version_number;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
get_version_number,
|
|
||||||
tokio::sync::{
|
tokio::sync::{
|
||||||
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||||
Mutex as TokioMutex,
|
Mutex as TokioMutex,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user