mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-12-02 10:49:21 +08:00
Merge pull request #1840 from fufesou/win_fix_multi_tab
Win fix multi tab
This commit is contained in:
commit
cf1ba283b4
@ -43,7 +43,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
tabController.onRemove = (_, id) => onRemoveId(id);
|
||||
tabController.onRemoved = (_, id) => onRemoveId(id);
|
||||
|
||||
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
||||
print(
|
||||
|
@ -46,7 +46,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
tabController.onRemove = (_, id) => onRemoveId(id);
|
||||
tabController.onRemoved = (_, id) => onRemoveId(id);
|
||||
|
||||
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
||||
debugPrint(
|
||||
|
@ -24,8 +24,9 @@ class ConnectionTabPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
final tabController =
|
||||
Get.put(DesktopTabController(tabType: DesktopTabType.remoteScreen));
|
||||
final tabController = Get.put(DesktopTabController(
|
||||
tabType: DesktopTabType.remoteScreen,
|
||||
onSelected: (_, id) => bind.setCurSessionId(id: id)));
|
||||
static const IconData selectedIcon = Icons.desktop_windows_sharp;
|
||||
static const IconData unselectedIcon = Icons.desktop_windows_outlined;
|
||||
|
||||
@ -59,11 +60,11 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
tabController.onRemove = (_, id) => onRemoveId(id);
|
||||
tabController.onRemoved = (_, id) => onRemoveId(id);
|
||||
|
||||
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
||||
print(
|
||||
"call ${call.method} with args ${call.arguments} from window ${fromWindowId}");
|
||||
"call ${call.method} with args ${call.arguments} from window $fromWindowId");
|
||||
|
||||
final RxBool fullscreen = Get.find(tag: 'fullscreen');
|
||||
// for simplify, just replace connectionId
|
||||
|
@ -30,7 +30,7 @@ class _DesktopServerPageState extends State<DesktopServerPage>
|
||||
void initState() {
|
||||
gFFI.ffiModel.updateEventListener("");
|
||||
windowManager.addListener(this);
|
||||
tabController.onRemove = (_, id) => onRemoveId(id);
|
||||
tabController.onRemoved = (_, id) => onRemoveId(id);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ class ConnectionManagerState extends State<ConnectionManager> {
|
||||
@override
|
||||
void initState() {
|
||||
gFFI.serverModel.updateClientState();
|
||||
gFFI.serverModel.tabController.onSelected = (index) =>
|
||||
gFFI.serverModel.tabController.onSelected = (index, _) =>
|
||||
gFFI.chatModel.changeCurrentID(gFFI.serverModel.clients[index].id);
|
||||
super.initState();
|
||||
}
|
||||
|
@ -70,10 +70,11 @@ class DesktopTabController {
|
||||
final DesktopTabType tabType;
|
||||
|
||||
/// index, key
|
||||
Function(int, String)? onRemove;
|
||||
Function(int)? onSelected;
|
||||
Function(int, String)? onRemoved;
|
||||
Function(int, String)? onSelected;
|
||||
|
||||
DesktopTabController({required this.tabType});
|
||||
DesktopTabController(
|
||||
{required this.tabType, this.onRemoved, this.onSelected});
|
||||
|
||||
int get length => state.value.tabs.length;
|
||||
|
||||
@ -114,7 +115,7 @@ class DesktopTabController {
|
||||
state.value.tabs.removeAt(index);
|
||||
state.value.scrollController.itemCount = state.value.tabs.length;
|
||||
jumpTo(toIndex);
|
||||
onRemove?.call(index, key);
|
||||
onRemoved?.call(index, key);
|
||||
}
|
||||
|
||||
void jumpTo(int index) {
|
||||
@ -134,7 +135,8 @@ class DesktopTabController {
|
||||
}));
|
||||
});
|
||||
if (state.value.tabs.length > index) {
|
||||
onSelected?.call(index);
|
||||
final key = state.value.tabs[index].key;
|
||||
onSelected?.call(index, key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -146,7 +148,7 @@ class DesktopTabController {
|
||||
|
||||
void closeBy(String? key) {
|
||||
if (!isDesktop) return;
|
||||
assert(onRemove != null);
|
||||
assert(onRemoved != null);
|
||||
if (key == null) {
|
||||
if (state.value.selected < state.value.tabs.length) {
|
||||
remove(state.value.selected);
|
||||
|
@ -28,7 +28,7 @@ import 'input_model.dart';
|
||||
import 'platform_model.dart';
|
||||
|
||||
typedef HandleMsgBox = Function(Map<String, dynamic> evt, String id);
|
||||
bool _waitForImage = false;
|
||||
final _waitForImage = <String, bool>{};
|
||||
|
||||
class FfiModel with ChangeNotifier {
|
||||
PeerInfo _pi = PeerInfo();
|
||||
@ -92,7 +92,6 @@ class FfiModel with ChangeNotifier {
|
||||
clear() {
|
||||
_pi = PeerInfo();
|
||||
_display = Display();
|
||||
_waitForImage = false;
|
||||
_secure = null;
|
||||
_direct = null;
|
||||
_inputBlocked = false;
|
||||
@ -314,7 +313,7 @@ class FfiModel with ChangeNotifier {
|
||||
parent.target?.dialogManager.showLoading(
|
||||
translate('Connected, waiting for image...'),
|
||||
onCancel: closeConnection);
|
||||
_waitForImage = true;
|
||||
_waitForImage[peerId] = true;
|
||||
_reconnects = 1;
|
||||
}
|
||||
}
|
||||
@ -354,8 +353,8 @@ class ImageModel with ChangeNotifier {
|
||||
ImageModel(this.parent);
|
||||
|
||||
onRgba(Uint8List rgba) {
|
||||
if (_waitForImage) {
|
||||
_waitForImage = false;
|
||||
if (_waitForImage[id]!) {
|
||||
_waitForImage[id] = false;
|
||||
parent.target?.dialogManager.dismissAll();
|
||||
}
|
||||
final pid = parent.target?.id;
|
||||
|
59
libs/clipboard/src/context_send.rs
Normal file
59
libs/clipboard/src/context_send.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use crate::cliprdr::*;
|
||||
use hbb_common::log;
|
||||
use std::sync::Mutex;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref CONTEXT_SEND: ContextSend = ContextSend{addr: Mutex::new(0)};
|
||||
}
|
||||
|
||||
pub struct ContextSend {
|
||||
addr: Mutex<u64>,
|
||||
}
|
||||
|
||||
impl ContextSend {
|
||||
pub fn is_enabled() -> bool {
|
||||
*CONTEXT_SEND.addr.lock().unwrap() != 0
|
||||
}
|
||||
|
||||
pub fn enable(enabled: bool) {
|
||||
let mut lock = CONTEXT_SEND.addr.lock().unwrap();
|
||||
if enabled {
|
||||
if *lock == 0 {
|
||||
match crate::create_cliprdr_context(true, false, crate::ProcessSide::ClientSide) {
|
||||
Ok(context) => {
|
||||
log::info!("clipboard context for file transfer created.");
|
||||
*lock = Box::into_raw(context) as _;
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!(
|
||||
"Create clipboard context for file transfer: {}",
|
||||
err.to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if *lock != 0 {
|
||||
unsafe {
|
||||
let _ = Box::from_raw(*lock as *mut CliprdrClientContext);
|
||||
}
|
||||
log::info!("clipboard context for file transfer destroyed.");
|
||||
*lock = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn proc<F: FnOnce(&mut Box<CliprdrClientContext>) -> u32>(f: F) -> u32 {
|
||||
let mut lock = CONTEXT_SEND.addr.lock().unwrap();
|
||||
if *lock != 0 {
|
||||
unsafe {
|
||||
let mut context = Box::from_raw(*lock as *mut CliprdrClientContext);
|
||||
let res = f(&mut context);
|
||||
*lock = Box::into_raw(context) as _;
|
||||
res
|
||||
}
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use cliprdr::*;
|
||||
use hbb_common::{
|
||||
log,
|
||||
allow_err, log,
|
||||
tokio::sync::{
|
||||
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||
Mutex as TokioMutex,
|
||||
@ -12,33 +12,31 @@ use std::{
|
||||
boxed::Box,
|
||||
collections::HashMap,
|
||||
ffi::{CStr, CString},
|
||||
sync::Mutex,
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
};
|
||||
|
||||
pub mod cliprdr;
|
||||
pub mod context_send;
|
||||
pub use context_send::*;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[serde(tag = "t", content = "c")]
|
||||
pub enum ClipbaordFile {
|
||||
ServerFormatList {
|
||||
conn_id: i32,
|
||||
MonitorReady,
|
||||
FormatList {
|
||||
format_list: Vec<(i32, String)>,
|
||||
},
|
||||
ServerFormatListResponse {
|
||||
conn_id: i32,
|
||||
FormatListResponse {
|
||||
msg_flags: i32,
|
||||
},
|
||||
ServerFormatDataRequest {
|
||||
conn_id: i32,
|
||||
FormatDataRequest {
|
||||
requested_format_id: i32,
|
||||
},
|
||||
ServerFormatDataResponse {
|
||||
conn_id: i32,
|
||||
FormatDataResponse {
|
||||
msg_flags: i32,
|
||||
format_data: Vec<u8>,
|
||||
},
|
||||
FileContentsRequest {
|
||||
conn_id: i32,
|
||||
stream_id: i32,
|
||||
list_index: i32,
|
||||
dw_flags: i32,
|
||||
@ -49,7 +47,6 @@ pub enum ClipbaordFile {
|
||||
clip_data_id: i32,
|
||||
},
|
||||
FileContentsResponse {
|
||||
conn_id: i32,
|
||||
msg_flags: i32,
|
||||
stream_id: i32,
|
||||
requested_data: Vec<u8>,
|
||||
@ -61,18 +58,89 @@ struct ConnEnabled {
|
||||
conn_enabled: HashMap<i32, bool>,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref MSG_CHANNEL_CLIENT: (UnboundedSender<(i32, ClipbaordFile)>, TokioMutex<UnboundedReceiver<(i32, ClipbaordFile)>>) = {
|
||||
let (tx, rx) = unbounded_channel();
|
||||
(tx, TokioMutex::new(rx))
|
||||
};
|
||||
|
||||
static ref CLIP_CONN_ENABLED: Mutex<ConnEnabled> = Mutex::new(ConnEnabled::default());
|
||||
struct MsgChannel {
|
||||
peer_id: String,
|
||||
conn_id: i32,
|
||||
sender: UnboundedSender<ClipbaordFile>,
|
||||
receiver: Arc<TokioMutex<UnboundedReceiver<ClipbaordFile>>>,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_rx_clip_client<'a>() -> &'a TokioMutex<UnboundedReceiver<(i32, ClipbaordFile)>> {
|
||||
&MSG_CHANNEL_CLIENT.1
|
||||
#[derive(PartialEq)]
|
||||
pub enum ProcessSide {
|
||||
UnknownSide,
|
||||
ClientSide,
|
||||
ServerSide,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref VEC_MSG_CHANNEL: RwLock<Vec<MsgChannel>> = Default::default();
|
||||
static ref CLIP_CONN_ENABLED: Mutex<ConnEnabled> = Mutex::new(ConnEnabled::default());
|
||||
static ref PROCESS_SIDE: RwLock<ProcessSide> = RwLock::new(ProcessSide::UnknownSide);
|
||||
}
|
||||
|
||||
pub fn get_client_conn_id(peer_id: &str) -> Option<i32> {
|
||||
VEC_MSG_CHANNEL
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find(|x| x.peer_id == peer_id.to_owned())
|
||||
.map(|x| x.conn_id)
|
||||
}
|
||||
|
||||
pub fn get_rx_cliprdr_client(
|
||||
peer_id: &str,
|
||||
) -> (i32, Arc<TokioMutex<UnboundedReceiver<ClipbaordFile>>>) {
|
||||
let mut lock = VEC_MSG_CHANNEL.write().unwrap();
|
||||
match lock.iter().find(|x| x.peer_id == peer_id.to_owned()) {
|
||||
Some(msg_channel) => (msg_channel.conn_id, msg_channel.receiver.clone()),
|
||||
None => {
|
||||
let (sender, receiver) = unbounded_channel();
|
||||
let receiver = Arc::new(TokioMutex::new(receiver));
|
||||
let receiver2 = receiver.clone();
|
||||
let conn_id = lock.len() as i32 + 1;
|
||||
let msg_channel = MsgChannel {
|
||||
peer_id: peer_id.to_owned(),
|
||||
conn_id,
|
||||
sender,
|
||||
receiver,
|
||||
};
|
||||
lock.push(msg_channel);
|
||||
(conn_id, receiver2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rx_cliprdr_server(conn_id: i32) -> Arc<TokioMutex<UnboundedReceiver<ClipbaordFile>>> {
|
||||
let mut lock = VEC_MSG_CHANNEL.write().unwrap();
|
||||
match lock.iter().find(|x| x.conn_id == conn_id) {
|
||||
Some(msg_channel) => msg_channel.receiver.clone(),
|
||||
None => {
|
||||
let (sender, receiver) = unbounded_channel();
|
||||
let receiver = Arc::new(TokioMutex::new(receiver));
|
||||
let receiver2 = receiver.clone();
|
||||
let msg_channel = MsgChannel {
|
||||
peer_id: "".to_owned(),
|
||||
conn_id,
|
||||
sender,
|
||||
receiver,
|
||||
};
|
||||
lock.push(msg_channel);
|
||||
receiver2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn send_data(conn_id: i32, data: ClipbaordFile) {
|
||||
// no need to handle result here
|
||||
if let Some(msg_channel) = VEC_MSG_CHANNEL
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find(|x| x.conn_id == conn_id)
|
||||
{
|
||||
allow_err!(msg_channel.sender.send(data));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_conn_enabled(conn_id: i32, enabled: bool) {
|
||||
@ -88,61 +156,46 @@ pub fn empty_clipboard(context: &mut Box<CliprdrClientContext>, conn_id: i32) ->
|
||||
|
||||
pub fn server_clip_file(
|
||||
context: &mut Box<CliprdrClientContext>,
|
||||
s_conn_id: i32,
|
||||
conn_id: i32,
|
||||
msg: ClipbaordFile,
|
||||
) -> u32 {
|
||||
match msg {
|
||||
ClipbaordFile::ServerFormatList {
|
||||
mut conn_id,
|
||||
format_list,
|
||||
} => {
|
||||
if s_conn_id != 0 {
|
||||
conn_id = s_conn_id as i32;
|
||||
}
|
||||
ClipbaordFile::MonitorReady => {
|
||||
log::debug!("server_monitor_ready called");
|
||||
let ret = server_monitor_ready(context, conn_id);
|
||||
log::debug!("server_monitor_ready called, return {}", ret);
|
||||
ret
|
||||
}
|
||||
ClipbaordFile::FormatList { format_list } => {
|
||||
log::debug!("server_format_list called");
|
||||
let ret = server_format_list(context, conn_id, format_list);
|
||||
log::debug!("server_format_list called, return {}", ret);
|
||||
ret
|
||||
}
|
||||
ClipbaordFile::ServerFormatListResponse {
|
||||
mut conn_id,
|
||||
msg_flags,
|
||||
} => {
|
||||
if s_conn_id != 0 {
|
||||
conn_id = s_conn_id as i32;
|
||||
}
|
||||
ClipbaordFile::FormatListResponse { msg_flags } => {
|
||||
log::debug!("format_list_response called");
|
||||
let ret = server_format_list_response(context, conn_id, msg_flags);
|
||||
log::debug!("server_format_list_response called, return {}", ret);
|
||||
ret
|
||||
}
|
||||
ClipbaordFile::ServerFormatDataRequest {
|
||||
mut conn_id,
|
||||
ClipbaordFile::FormatDataRequest {
|
||||
requested_format_id,
|
||||
} => {
|
||||
if s_conn_id != 0 {
|
||||
conn_id = s_conn_id as i32;
|
||||
}
|
||||
log::debug!("format_data_request called");
|
||||
let ret = server_format_data_request(context, conn_id, requested_format_id);
|
||||
log::debug!("server_format_data_request called, return {}", ret);
|
||||
ret
|
||||
}
|
||||
ClipbaordFile::ServerFormatDataResponse {
|
||||
mut conn_id,
|
||||
ClipbaordFile::FormatDataResponse {
|
||||
msg_flags,
|
||||
format_data,
|
||||
} => {
|
||||
if s_conn_id != 0 {
|
||||
conn_id = s_conn_id as i32;
|
||||
}
|
||||
log::debug!("format_data_response called");
|
||||
let ret = server_format_data_response(context, conn_id, msg_flags, format_data);
|
||||
log::debug!("server_format_data_response called, return {}", ret);
|
||||
ret
|
||||
}
|
||||
ClipbaordFile::FileContentsRequest {
|
||||
mut conn_id,
|
||||
stream_id,
|
||||
list_index,
|
||||
dw_flags,
|
||||
@ -152,9 +205,6 @@ pub fn server_clip_file(
|
||||
have_clip_data_id,
|
||||
clip_data_id,
|
||||
} => {
|
||||
if s_conn_id != 0 {
|
||||
conn_id = s_conn_id as i32;
|
||||
}
|
||||
log::debug!("file_contents_request called");
|
||||
let ret = server_file_contents_request(
|
||||
context,
|
||||
@ -172,14 +222,10 @@ pub fn server_clip_file(
|
||||
ret
|
||||
}
|
||||
ClipbaordFile::FileContentsResponse {
|
||||
mut conn_id,
|
||||
msg_flags,
|
||||
stream_id,
|
||||
requested_data,
|
||||
} => {
|
||||
if s_conn_id != 0 {
|
||||
conn_id = s_conn_id as i32;
|
||||
}
|
||||
log::debug!("file_contents_response called");
|
||||
let ret = server_file_contents_response(
|
||||
context,
|
||||
@ -194,6 +240,19 @@ pub fn server_clip_file(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_monitor_ready(context: &mut Box<CliprdrClientContext>, conn_id: i32) -> u32 {
|
||||
unsafe {
|
||||
let monitor_ready = CLIPRDR_MONITOR_READY {
|
||||
connID: conn_id as UINT32,
|
||||
msgType: 0 as UINT16,
|
||||
msgFlags: 0 as UINT16,
|
||||
dataLen: 0 as UINT32,
|
||||
};
|
||||
let ret = ((**context).MonitorReady.unwrap())(&mut (**context), &monitor_ready);
|
||||
ret as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_format_list(
|
||||
context: &mut Box<CliprdrClientContext>,
|
||||
conn_id: i32,
|
||||
@ -243,6 +302,7 @@ pub fn server_format_list(
|
||||
ret as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_format_list_response(
|
||||
context: &mut Box<CliprdrClientContext>,
|
||||
conn_id: i32,
|
||||
@ -262,6 +322,7 @@ pub fn server_format_list_response(
|
||||
ret as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_format_data_request(
|
||||
context: &mut Box<CliprdrClientContext>,
|
||||
conn_id: i32,
|
||||
@ -280,6 +341,7 @@ pub fn server_format_data_request(
|
||||
ret as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_format_data_response(
|
||||
context: &mut Box<CliprdrClientContext>,
|
||||
conn_id: i32,
|
||||
@ -301,6 +363,7 @@ pub fn server_format_data_response(
|
||||
ret as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_file_contents_request(
|
||||
context: &mut Box<CliprdrClientContext>,
|
||||
conn_id: i32,
|
||||
@ -335,6 +398,7 @@ pub fn server_file_contents_request(
|
||||
ret as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_file_contents_response(
|
||||
context: &mut Box<CliprdrClientContext>,
|
||||
conn_id: i32,
|
||||
@ -363,7 +427,10 @@ pub fn server_file_contents_response(
|
||||
pub fn create_cliprdr_context(
|
||||
enable_files: bool,
|
||||
enable_others: bool,
|
||||
process_side: ProcessSide,
|
||||
) -> ResultType<Box<CliprdrClientContext>> {
|
||||
*PROCESS_SIDE.write().unwrap() = process_side;
|
||||
|
||||
Ok(CliprdrClientContext::create(
|
||||
enable_files,
|
||||
enable_others,
|
||||
@ -378,8 +445,11 @@ pub fn create_cliprdr_context(
|
||||
}
|
||||
|
||||
extern "C" fn check_enabled(conn_id: UINT32) -> BOOL {
|
||||
let lock = CLIP_CONN_ENABLED.lock().unwrap();
|
||||
if *PROCESS_SIDE.read().unwrap() == ProcessSide::ClientSide {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
let lock = CLIP_CONN_ENABLED.lock().unwrap();
|
||||
let mut connd_enabled = false;
|
||||
if conn_id != 0 {
|
||||
if let Some(true) = lock.conn_enabled.get(&(conn_id as i32)) {
|
||||
@ -422,12 +492,17 @@ extern "C" fn client_format_list(
|
||||
}
|
||||
conn_id = (*clip_format_list).connID as i32;
|
||||
}
|
||||
let data = ClipbaordFile::ServerFormatList {
|
||||
conn_id,
|
||||
format_list,
|
||||
};
|
||||
let data = ClipbaordFile::FormatList { format_list };
|
||||
// no need to handle result here
|
||||
MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap();
|
||||
if conn_id == 0 {
|
||||
VEC_MSG_CHANNEL
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.for_each(|msg_channel| allow_err!(msg_channel.sender.send(data.clone())));
|
||||
} else {
|
||||
send_data(conn_id, data);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
@ -444,9 +519,8 @@ extern "C" fn client_format_list_response(
|
||||
conn_id = (*format_list_response).connID as i32;
|
||||
msg_flags = (*format_list_response).msgFlags as i32;
|
||||
}
|
||||
let data = ClipbaordFile::ServerFormatListResponse { conn_id, msg_flags };
|
||||
// no need to handle result here
|
||||
MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap();
|
||||
let data = ClipbaordFile::FormatListResponse { msg_flags };
|
||||
send_data(conn_id, data);
|
||||
|
||||
0
|
||||
}
|
||||
@ -463,12 +537,11 @@ extern "C" fn client_format_data_request(
|
||||
conn_id = (*format_data_request).connID as i32;
|
||||
requested_format_id = (*format_data_request).requestedFormatId as i32;
|
||||
}
|
||||
let data = ClipbaordFile::ServerFormatDataRequest {
|
||||
conn_id,
|
||||
let data = ClipbaordFile::FormatDataRequest {
|
||||
requested_format_id,
|
||||
};
|
||||
// no need to handle result here
|
||||
MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap();
|
||||
send_data(conn_id, data);
|
||||
|
||||
0
|
||||
}
|
||||
@ -495,13 +568,11 @@ extern "C" fn client_format_data_response(
|
||||
.to_vec();
|
||||
}
|
||||
}
|
||||
let data = ClipbaordFile::ServerFormatDataResponse {
|
||||
conn_id,
|
||||
let data = ClipbaordFile::FormatDataResponse {
|
||||
msg_flags,
|
||||
format_data,
|
||||
};
|
||||
// no need to handle result here
|
||||
MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap();
|
||||
send_data(conn_id, data);
|
||||
|
||||
0
|
||||
}
|
||||
@ -544,7 +615,6 @@ extern "C" fn client_file_contents_request(
|
||||
}
|
||||
|
||||
let data = ClipbaordFile::FileContentsRequest {
|
||||
conn_id,
|
||||
stream_id,
|
||||
list_index,
|
||||
dw_flags,
|
||||
@ -554,8 +624,7 @@ extern "C" fn client_file_contents_request(
|
||||
have_clip_data_id,
|
||||
clip_data_id,
|
||||
};
|
||||
// no need to handle result here
|
||||
MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap();
|
||||
send_data(conn_id, data);
|
||||
|
||||
0
|
||||
}
|
||||
@ -585,13 +654,11 @@ extern "C" fn client_file_contents_response(
|
||||
}
|
||||
}
|
||||
let data = ClipbaordFile::FileContentsResponse {
|
||||
conn_id,
|
||||
msg_flags,
|
||||
stream_id,
|
||||
requested_data,
|
||||
};
|
||||
// no need to handle result here
|
||||
MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap();
|
||||
send_data(conn_id, data);
|
||||
|
||||
0
|
||||
}
|
||||
|
@ -135,14 +135,14 @@ typedef struct _FORMAT_IDS FORMAT_IDS;
|
||||
|
||||
#define TAG "windows"
|
||||
|
||||
#ifdef WITH_DEBUG_CLIPRDR
|
||||
#define DEBUG_CLIPRDR(...) printf(TAG, __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_CLIPRDR(...) \
|
||||
do \
|
||||
{ \
|
||||
} while (0)
|
||||
#endif
|
||||
// #ifdef WITH_DEBUG_CLIPRDR
|
||||
#define DEBUG_CLIPRDR(fmt, ...) fprintf(stderr, "DEBUG %s[%d] %s() " fmt "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__);fflush(stderr)
|
||||
// #else
|
||||
// #define DEBUG_CLIPRDR(fmt, ...) \
|
||||
// do \
|
||||
// { \
|
||||
// } while (0)
|
||||
// #endif
|
||||
|
||||
typedef BOOL(WINAPI *fnAddClipboardFormatListener)(HWND hwnd);
|
||||
typedef BOOL(WINAPI *fnRemoveClipboardFormatListener)(HWND hwnd);
|
||||
@ -193,6 +193,7 @@ struct _CliprdrDataObject
|
||||
ULONG m_nStreams;
|
||||
IStream **m_pStream;
|
||||
void *m_pData;
|
||||
DWORD m_processID;
|
||||
UINT32 m_connID;
|
||||
};
|
||||
typedef struct _CliprdrDataObject CliprdrDataObject;
|
||||
@ -246,7 +247,7 @@ BOOL wf_cliprdr_init(wfClipboard *clipboard, CliprdrClientContext *cliprdr);
|
||||
BOOL wf_cliprdr_uninit(wfClipboard *clipboard, CliprdrClientContext *cliprdr);
|
||||
BOOL wf_do_empty_cliprdr(wfClipboard *clipboard);
|
||||
|
||||
static BOOL wf_create_file_obj(UINT32 connID, wfClipboard *cliprdrrdr, IDataObject **ppDataObject);
|
||||
static BOOL wf_create_file_obj(UINT32 *connID, wfClipboard *clipboard, IDataObject **ppDataObject);
|
||||
static void wf_destroy_file_obj(IDataObject *instance);
|
||||
static UINT32 get_remote_format_id(wfClipboard *clipboard, UINT32 local_format);
|
||||
static UINT cliprdr_send_data_request(UINT32 connID, wfClipboard *clipboard, UINT32 format);
|
||||
@ -673,6 +674,12 @@ static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetData(IDataObject *This, FO
|
||||
if (!pFormatEtc || !pMedium || !instance)
|
||||
return E_INVALIDARG;
|
||||
|
||||
// Not the same process id
|
||||
if (instance->m_processID != GetCurrentProcessId())
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
clipboard = (wfClipboard *)instance->m_pData;
|
||||
if (!clipboard->context->CheckEnabled(instance->m_connID))
|
||||
{
|
||||
@ -892,6 +899,7 @@ static CliprdrDataObject *CliprdrDataObject_New(UINT32 connID, FORMATETC *fmtetc
|
||||
instance->m_pData = data;
|
||||
instance->m_nStreams = 0;
|
||||
instance->m_pStream = NULL;
|
||||
instance->m_processID = GetCurrentProcessId();
|
||||
instance->m_connID = connID;
|
||||
|
||||
if (count > 0)
|
||||
@ -1340,7 +1348,7 @@ static BOOL cliprdr_GetUpdatedClipboardFormats(wfClipboard *clipboard, PUINT lpu
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT cliprdr_send_format_list(wfClipboard *clipboard)
|
||||
static UINT cliprdr_send_format_list(wfClipboard *clipboard, UINT32 connID)
|
||||
{
|
||||
UINT rc;
|
||||
int count = 0;
|
||||
@ -1415,7 +1423,7 @@ static UINT cliprdr_send_format_list(wfClipboard *clipboard)
|
||||
}
|
||||
}
|
||||
|
||||
formatList.connID = 0;
|
||||
formatList.connID = connID;
|
||||
formatList.numFormats = numFormats;
|
||||
formatList.formats = formats;
|
||||
formatList.msgType = CB_FORMAT_LIST;
|
||||
@ -1645,7 +1653,7 @@ static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM
|
||||
clipboard->hmem = NULL;
|
||||
}
|
||||
|
||||
cliprdr_send_format_list(clipboard);
|
||||
cliprdr_send_format_list(clipboard, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1669,7 +1677,8 @@ static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM
|
||||
DEBUG_CLIPRDR("info: WM_RENDERFORMAT");
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/dataxchg/wm-renderformat?redirectedfrom=MSDN
|
||||
if (cliprdr_send_data_request(0, 0, clipboard, (UINT32)wParam) != 0)
|
||||
// to-do: ensure usage of 0
|
||||
if (cliprdr_send_data_request(0, clipboard, (UINT32)wParam) != 0)
|
||||
{
|
||||
DEBUG_CLIPRDR("error: cliprdr_send_data_request failed.");
|
||||
break;
|
||||
@ -1695,7 +1704,7 @@ static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM
|
||||
if ((GetClipboardOwner() != clipboard->hwnd) &&
|
||||
(S_FALSE == OleIsCurrentClipboard(clipboard->data_obj)))
|
||||
{
|
||||
cliprdr_send_format_list(clipboard);
|
||||
cliprdr_send_format_list(clipboard, 0);
|
||||
}
|
||||
|
||||
SendMessage(clipboard->hWndNextViewer, Msg, wParam, lParam);
|
||||
@ -2128,9 +2137,14 @@ static UINT wf_cliprdr_send_client_capabilities(wfClipboard *clipboard)
|
||||
CLIPRDR_CAPABILITIES capabilities;
|
||||
CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
|
||||
|
||||
if (!clipboard || !clipboard->context || !clipboard->context->ClientCapabilities)
|
||||
if (!clipboard || !clipboard->context)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
// Ignore ClientCapabilities for now
|
||||
if (!clipboard->context->ClientCapabilities) {
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
capabilities.connID = 0;
|
||||
capabilities.cCapabilitiesSets = 1;
|
||||
capabilities.capabilitySets = (CLIPRDR_CAPABILITY_SET *)&(generalCapabilitySet);
|
||||
@ -2162,7 +2176,7 @@ static UINT wf_cliprdr_monitor_ready(CliprdrClientContext *context,
|
||||
if (rc != CHANNEL_RC_OK)
|
||||
return rc;
|
||||
|
||||
return cliprdr_send_format_list(clipboard);
|
||||
return cliprdr_send_format_list(clipboard, monitorReady->connID);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -360,38 +360,31 @@ message FileDirCreate {
|
||||
|
||||
// main logic from freeRDP
|
||||
message CliprdrMonitorReady {
|
||||
int32 conn_id = 1;
|
||||
}
|
||||
|
||||
message CliprdrFormat {
|
||||
int32 conn_id = 1;
|
||||
int32 id = 2;
|
||||
string format = 3;
|
||||
}
|
||||
|
||||
message CliprdrServerFormatList {
|
||||
int32 conn_id = 1;
|
||||
repeated CliprdrFormat formats = 2;
|
||||
}
|
||||
|
||||
message CliprdrServerFormatListResponse {
|
||||
int32 conn_id = 1;
|
||||
int32 msg_flags = 2;
|
||||
}
|
||||
|
||||
message CliprdrServerFormatDataRequest {
|
||||
int32 conn_id = 1;
|
||||
int32 requested_format_id = 2;
|
||||
}
|
||||
|
||||
message CliprdrServerFormatDataResponse {
|
||||
int32 conn_id = 1;
|
||||
int32 msg_flags = 2;
|
||||
bytes format_data = 3;
|
||||
}
|
||||
|
||||
message CliprdrFileContentsRequest {
|
||||
int32 conn_id = 1;
|
||||
int32 stream_id = 2;
|
||||
int32 list_index = 3;
|
||||
int32 dw_flags = 4;
|
||||
@ -403,7 +396,6 @@ message CliprdrFileContentsRequest {
|
||||
}
|
||||
|
||||
message CliprdrFileContentsResponse {
|
||||
int32 conn_id = 1;
|
||||
int32 msg_flags = 3;
|
||||
int32 stream_id = 4;
|
||||
bytes requested_data = 5;
|
||||
|
@ -6,6 +6,9 @@ use crate::common;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
use crate::common::{check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL};
|
||||
|
||||
#[cfg(windows)]
|
||||
use clipboard::{cliprdr::CliprdrClientContext, ContextSend};
|
||||
|
||||
use crate::ui_session_interface::{InvokeUiSession, Session};
|
||||
use crate::{client::Data, client::Interface};
|
||||
|
||||
@ -22,7 +25,11 @@ use hbb_common::tokio::{
|
||||
sync::mpsc,
|
||||
time::{self, Duration, Instant, Interval},
|
||||
};
|
||||
use hbb_common::{allow_err, message_proto::*, sleep};
|
||||
use hbb_common::{
|
||||
allow_err,
|
||||
message_proto::{self, *},
|
||||
sleep,
|
||||
};
|
||||
use hbb_common::{fs, log, Stream};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -43,7 +50,7 @@ pub struct Remote<T: InvokeUiSession> {
|
||||
last_update_jobs_status: (Instant, HashMap<i32, u64>),
|
||||
first_frame: bool,
|
||||
#[cfg(windows)]
|
||||
clipboard_file_context: Option<Box<clipboard::cliprdr::CliprdrClientContext>>,
|
||||
client_conn_id: i32, // used for clipboard
|
||||
data_count: Arc<AtomicUsize>,
|
||||
frame_count: Arc<AtomicUsize>,
|
||||
video_format: CodecFormat,
|
||||
@ -72,7 +79,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
last_update_jobs_status: (Instant::now(), Default::default()),
|
||||
first_frame: false,
|
||||
#[cfg(windows)]
|
||||
clipboard_file_context: None,
|
||||
client_conn_id: 0,
|
||||
data_count: Arc::new(AtomicUsize::new(0)),
|
||||
frame_count,
|
||||
video_format: CodecFormat::Unknown,
|
||||
@ -107,7 +114,14 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
#[cfg(not(windows))]
|
||||
let (_tx_holder, mut rx_clip_client) = mpsc::unbounded_channel::<i32>();
|
||||
#[cfg(windows)]
|
||||
let mut rx_clip_client = clipboard::get_rx_clip_client().lock().await;
|
||||
let (client_conn_id, rx_clip_client1) =
|
||||
clipboard::get_rx_cliprdr_client(&self.handler.id);
|
||||
#[cfg(windows)]
|
||||
let mut rx_clip_client = rx_clip_client1.lock().await;
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self.client_conn_id = client_conn_id;
|
||||
}
|
||||
|
||||
let mut status_timer = time::interval(Duration::new(1, 0));
|
||||
|
||||
@ -152,7 +166,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
_msg = rx_clip_client.recv() => {
|
||||
#[cfg(windows)]
|
||||
match _msg {
|
||||
Some((_, clip)) => {
|
||||
Some(clip) => {
|
||||
allow_err!(peer.send(&crate::clipboard_file::clip_2_msg(clip)).await);
|
||||
}
|
||||
None => {
|
||||
@ -778,13 +792,7 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
#[cfg(windows)]
|
||||
Some(message::Union::Cliprdr(clip)) => {
|
||||
if !self.handler.lc.read().unwrap().disable_clipboard {
|
||||
if let Some(context) = &mut self.clipboard_file_context {
|
||||
if let Some(clip) = crate::clipboard_file::msg_2_clip(clip) {
|
||||
clipboard::server_clip_file(context, 0, clip);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.handle_cliprdr_msg(clip);
|
||||
}
|
||||
Some(message::Union::FileResponse(fr)) => {
|
||||
match fr.union {
|
||||
@ -1158,30 +1166,32 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
true
|
||||
}
|
||||
|
||||
fn check_clipboard_file_context(&mut self) {
|
||||
fn check_clipboard_file_context(&self) {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let enabled = SERVER_FILE_TRANSFER_ENABLED.load(Ordering::SeqCst)
|
||||
&& self.handler.lc.read().unwrap().enable_file_transfer;
|
||||
if enabled == self.clipboard_file_context.is_none() {
|
||||
self.clipboard_file_context = if enabled {
|
||||
match clipboard::create_cliprdr_context(true, false) {
|
||||
Ok(context) => {
|
||||
log::info!("clipboard context for file transfer created.");
|
||||
Some(context)
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!(
|
||||
"Create clipboard context for file transfer: {}",
|
||||
err.to_string()
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log::info!("clipboard context for file transfer destroyed.");
|
||||
None
|
||||
};
|
||||
ContextSend::enable(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn handle_cliprdr_msg(&self, clip: message_proto::Cliprdr) {
|
||||
if !self.handler.lc.read().unwrap().disable_clipboard {
|
||||
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||
if let Some(message_proto::cliprdr::Union::FormatList(_)) = &clip.union {
|
||||
if self.client_conn_id
|
||||
!= clipboard::get_client_conn_id(&crate::flutter::get_cur_session_id())
|
||||
.unwrap_or(0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(clip) = crate::clipboard_file::msg_2_clip(clip) {
|
||||
ContextSend::proc(|context: &mut Box<CliprdrClientContext>| -> u32 {
|
||||
clipboard::server_clip_file(context, self.client_conn_id, clip)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,19 @@ use hbb_common::message_proto::*;
|
||||
|
||||
pub fn clip_2_msg(clip: ClipbaordFile) -> Message {
|
||||
match clip {
|
||||
ClipbaordFile::ServerFormatList {
|
||||
conn_id,
|
||||
format_list,
|
||||
} => {
|
||||
ClipbaordFile::MonitorReady => Message {
|
||||
union: Some(message::Union::Cliprdr(Cliprdr {
|
||||
union: Some(cliprdr::Union::Ready(CliprdrMonitorReady {
|
||||
..Default::default()
|
||||
})),
|
||||
..Default::default()
|
||||
})),
|
||||
..Default::default()
|
||||
},
|
||||
ClipbaordFile::FormatList { format_list } => {
|
||||
let mut formats: Vec<CliprdrFormat> = Vec::new();
|
||||
for v in format_list.iter() {
|
||||
formats.push(CliprdrFormat {
|
||||
conn_id: 0,
|
||||
id: v.0,
|
||||
format: v.1.clone(),
|
||||
..Default::default()
|
||||
@ -19,7 +24,6 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message {
|
||||
Message {
|
||||
union: Some(message::Union::Cliprdr(Cliprdr {
|
||||
union: Some(cliprdr::Union::FormatList(CliprdrServerFormatList {
|
||||
conn_id,
|
||||
formats,
|
||||
..Default::default()
|
||||
})),
|
||||
@ -28,11 +32,10 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message {
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
ClipbaordFile::ServerFormatListResponse { conn_id, msg_flags } => Message {
|
||||
ClipbaordFile::FormatListResponse { msg_flags } => Message {
|
||||
union: Some(message::Union::Cliprdr(Cliprdr {
|
||||
union: Some(cliprdr::Union::FormatListResponse(
|
||||
CliprdrServerFormatListResponse {
|
||||
conn_id,
|
||||
msg_flags,
|
||||
..Default::default()
|
||||
},
|
||||
@ -41,14 +44,12 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message {
|
||||
})),
|
||||
..Default::default()
|
||||
},
|
||||
ClipbaordFile::ServerFormatDataRequest {
|
||||
conn_id,
|
||||
ClipbaordFile::FormatDataRequest {
|
||||
requested_format_id,
|
||||
} => Message {
|
||||
union: Some(message::Union::Cliprdr(Cliprdr {
|
||||
union: Some(cliprdr::Union::FormatDataRequest(
|
||||
CliprdrServerFormatDataRequest {
|
||||
conn_id,
|
||||
requested_format_id,
|
||||
..Default::default()
|
||||
},
|
||||
@ -57,15 +58,13 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message {
|
||||
})),
|
||||
..Default::default()
|
||||
},
|
||||
ClipbaordFile::ServerFormatDataResponse {
|
||||
conn_id,
|
||||
ClipbaordFile::FormatDataResponse {
|
||||
msg_flags,
|
||||
format_data,
|
||||
} => Message {
|
||||
union: Some(message::Union::Cliprdr(Cliprdr {
|
||||
union: Some(cliprdr::Union::FormatDataResponse(
|
||||
CliprdrServerFormatDataResponse {
|
||||
conn_id,
|
||||
msg_flags,
|
||||
format_data: format_data.into(),
|
||||
..Default::default()
|
||||
@ -76,7 +75,6 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message {
|
||||
..Default::default()
|
||||
},
|
||||
ClipbaordFile::FileContentsRequest {
|
||||
conn_id,
|
||||
stream_id,
|
||||
list_index,
|
||||
dw_flags,
|
||||
@ -89,7 +87,6 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message {
|
||||
union: Some(message::Union::Cliprdr(Cliprdr {
|
||||
union: Some(cliprdr::Union::FileContentsRequest(
|
||||
CliprdrFileContentsRequest {
|
||||
conn_id,
|
||||
stream_id,
|
||||
list_index,
|
||||
dw_flags,
|
||||
@ -106,7 +103,6 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message {
|
||||
..Default::default()
|
||||
},
|
||||
ClipbaordFile::FileContentsResponse {
|
||||
conn_id,
|
||||
msg_flags,
|
||||
stream_id,
|
||||
requested_data,
|
||||
@ -114,7 +110,6 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message {
|
||||
union: Some(message::Union::Cliprdr(Cliprdr {
|
||||
union: Some(cliprdr::Union::FileContentsResponse(
|
||||
CliprdrFileContentsResponse {
|
||||
conn_id,
|
||||
msg_flags,
|
||||
stream_id,
|
||||
requested_data: requested_data.into(),
|
||||
@ -130,38 +125,26 @@ pub fn clip_2_msg(clip: ClipbaordFile) -> Message {
|
||||
|
||||
pub fn msg_2_clip(msg: Cliprdr) -> Option<ClipbaordFile> {
|
||||
match msg.union {
|
||||
Some(cliprdr::Union::Ready(_)) => Some(ClipbaordFile::MonitorReady),
|
||||
Some(cliprdr::Union::FormatList(data)) => {
|
||||
let mut format_list: Vec<(i32, String)> = Vec::new();
|
||||
for v in data.formats.iter() {
|
||||
format_list.push((v.id, v.format.clone()));
|
||||
}
|
||||
Some(ClipbaordFile::ServerFormatList {
|
||||
conn_id: data.conn_id,
|
||||
format_list,
|
||||
})
|
||||
}
|
||||
Some(cliprdr::Union::FormatListResponse(data)) => {
|
||||
Some(ClipbaordFile::ServerFormatListResponse {
|
||||
conn_id: data.conn_id,
|
||||
msg_flags: data.msg_flags,
|
||||
})
|
||||
}
|
||||
Some(cliprdr::Union::FormatDataRequest(data)) => {
|
||||
Some(ClipbaordFile::ServerFormatDataRequest {
|
||||
conn_id: data.conn_id,
|
||||
requested_format_id: data.requested_format_id,
|
||||
})
|
||||
}
|
||||
Some(cliprdr::Union::FormatDataResponse(data)) => {
|
||||
Some(ClipbaordFile::ServerFormatDataResponse {
|
||||
conn_id: data.conn_id,
|
||||
msg_flags: data.msg_flags,
|
||||
format_data: data.format_data.into(),
|
||||
})
|
||||
Some(ClipbaordFile::FormatList { format_list })
|
||||
}
|
||||
Some(cliprdr::Union::FormatListResponse(data)) => Some(ClipbaordFile::FormatListResponse {
|
||||
msg_flags: data.msg_flags,
|
||||
}),
|
||||
Some(cliprdr::Union::FormatDataRequest(data)) => Some(ClipbaordFile::FormatDataRequest {
|
||||
requested_format_id: data.requested_format_id,
|
||||
}),
|
||||
Some(cliprdr::Union::FormatDataResponse(data)) => Some(ClipbaordFile::FormatDataResponse {
|
||||
msg_flags: data.msg_flags,
|
||||
format_data: data.format_data.into(),
|
||||
}),
|
||||
Some(cliprdr::Union::FileContentsRequest(data)) => {
|
||||
Some(ClipbaordFile::FileContentsRequest {
|
||||
conn_id: data.conn_id,
|
||||
stream_id: data.stream_id,
|
||||
list_index: data.list_index,
|
||||
dw_flags: data.dw_flags,
|
||||
@ -174,7 +157,6 @@ pub fn msg_2_clip(msg: Cliprdr) -> Option<ClipbaordFile> {
|
||||
}
|
||||
Some(cliprdr::Union::FileContentsResponse(data)) => {
|
||||
Some(ClipbaordFile::FileContentsResponse {
|
||||
conn_id: data.conn_id,
|
||||
msg_flags: data.msg_flags,
|
||||
stream_id: data.stream_id,
|
||||
requested_data: data.requested_data.into(),
|
||||
|
@ -22,7 +22,8 @@ pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer";
|
||||
pub(super) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward";
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref SESSIONS: RwLock<HashMap<String,Session<FlutterHandler>>> = Default::default();
|
||||
static ref CUR_SESSION_ID: RwLock<String> = Default::default();
|
||||
pub static ref SESSIONS: RwLock<HashMap<String, Session<FlutterHandler>>> = Default::default();
|
||||
pub static ref GLOBAL_EVENT_STREAM: RwLock<HashMap<String, StreamSink<String>>> = Default::default(); // rust to dart event channel
|
||||
}
|
||||
|
||||
@ -564,3 +565,13 @@ pub fn make_fd_flutter(id: i32, entries: &Vec<FileEntry>, only_count: bool) -> S
|
||||
m.insert("total_size".into(), json!(n as f64));
|
||||
serde_json::to_string(&m).unwrap_or("".into())
|
||||
}
|
||||
|
||||
pub fn get_cur_session_id() -> String {
|
||||
CUR_SESSION_ID.read().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn set_cur_session_id(id: String) {
|
||||
if get_cur_session_id() != id {
|
||||
*CUR_SESSION_ID.write().unwrap() = id;
|
||||
}
|
||||
}
|
||||
|
@ -1052,6 +1052,10 @@ pub fn main_update_me() -> SyncReturn<bool> {
|
||||
SyncReturn(true)
|
||||
}
|
||||
|
||||
pub fn set_cur_session_id(id: String) {
|
||||
super::flutter::set_cur_session_id(id)
|
||||
}
|
||||
|
||||
pub fn install_show_run_without_install() -> SyncReturn<bool> {
|
||||
SyncReturn(show_run_without_install())
|
||||
}
|
||||
|
@ -249,8 +249,8 @@ pub(super) fn release_resouce() {
|
||||
if *write_lock != 0 {
|
||||
let cap_display_info: *mut CapDisplayInfo = *write_lock as _;
|
||||
unsafe {
|
||||
let box_capturer = Box::from_raw((*cap_display_info).capturer.0);
|
||||
let box_cap_display_info = Box::from_raw(cap_display_info);
|
||||
let _box_capturer = Box::from_raw((*cap_display_info).capturer.0);
|
||||
let _box_cap_display_info = Box::from_raw(cap_display_info);
|
||||
*write_lock = 0;
|
||||
}
|
||||
}
|
||||
|
25
src/ui.rs
25
src/ui.rs
@ -53,11 +53,8 @@ fn check_connect_status(
|
||||
) {
|
||||
let status = Arc::new(Mutex::new((0, false, 0, "".to_owned())));
|
||||
let options = Arc::new(Mutex::new(Config::get_options()));
|
||||
let cloned = status.clone();
|
||||
let cloned_options = options.clone();
|
||||
let (tx, rx) = mpsc::unbounded_channel::<ipc::Data>();
|
||||
let password = Arc::new(Mutex::new(String::default()));
|
||||
let cloned_password = password.clone();
|
||||
std::thread::spawn(move || crate::ui_interface::check_connect_status_(reconnect, rx));
|
||||
(status, options, tx, password)
|
||||
}
|
||||
@ -514,17 +511,17 @@ impl UI {
|
||||
}
|
||||
|
||||
fn get_lan_peers(&self) -> String {
|
||||
let peers = get_lan_peers()
|
||||
.into_iter()
|
||||
.map(|mut peer| {
|
||||
(
|
||||
peer.remove("id").unwrap_or_default(),
|
||||
peer.remove("username").unwrap_or_default(),
|
||||
peer.remove("hostname").unwrap_or_default(),
|
||||
peer.remove("platform").unwrap_or_default(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<(String, String, String, String)>>();
|
||||
// let peers = get_lan_peers()
|
||||
// .into_iter()
|
||||
// .map(|mut peer| {
|
||||
// (
|
||||
// peer.remove("id").unwrap_or_default(),
|
||||
// peer.remove("username").unwrap_or_default(),
|
||||
// peer.remove("hostname").unwrap_or_default(),
|
||||
// peer.remove("platform").unwrap_or_default(),
|
||||
// )
|
||||
// })
|
||||
// .collect::<Vec<(String, String, String, String)>>();
|
||||
serde_json::to_string(&get_lan_peers()).unwrap_or_default()
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,6 @@
|
||||
use crate::ipc::start_pa;
|
||||
use crate::ui_cm_interface::{start_ipc, ConnectionManager, InvokeUiCM};
|
||||
|
||||
#[cfg(windows)]
|
||||
use clipboard::{
|
||||
create_cliprdr_context, empty_clipboard, get_rx_clip_client, server_clip_file, set_conn_enabled,
|
||||
};
|
||||
|
||||
use hbb_common::{allow_err, log};
|
||||
use sciter::{make_args, Element, Value, HELEMENT};
|
||||
use std::sync::Mutex;
|
||||
|
@ -14,12 +14,6 @@ use sciter::{
|
||||
Value,
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
use clipboard::{
|
||||
cliprdr::CliprdrClientContext, create_cliprdr_context as create_clipboard_file_context,
|
||||
get_rx_clip_client, server_clip_file,
|
||||
};
|
||||
|
||||
use hbb_common::{
|
||||
allow_err, fs::TransferJobMeta, log, message_proto::*, rendezvous_proto::ConnType,
|
||||
};
|
||||
|
@ -1,17 +1,22 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
#[cfg(windows)]
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
iter::FromIterator,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::{
|
||||
atomic::{AtomicI64, Ordering},
|
||||
RwLock,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
use clipboard::{cliprdr::CliprdrClientContext, empty_clipboard, ContextSend};
|
||||
use serde_derive::Serialize;
|
||||
|
||||
use crate::ipc::Data;
|
||||
use crate::ipc::{self, new_listener, Connection};
|
||||
use crate::ipc::{self, new_listener, Connection, Data};
|
||||
#[cfg(windows)]
|
||||
use hbb_common::tokio::sync::Mutex as TokioMutex;
|
||||
use hbb_common::{
|
||||
allow_err,
|
||||
config::Config,
|
||||
@ -22,7 +27,7 @@ use hbb_common::{
|
||||
protobuf::Message as _,
|
||||
tokio::{
|
||||
self,
|
||||
sync::mpsc::{self, UnboundedSender},
|
||||
sync::mpsc::{self, unbounded_channel, UnboundedSender},
|
||||
task::spawn_blocking,
|
||||
},
|
||||
};
|
||||
@ -46,8 +51,19 @@ pub struct Client {
|
||||
tx: UnboundedSender<Data>,
|
||||
}
|
||||
|
||||
struct IpcTaskRunner<T: InvokeUiCM> {
|
||||
stream: Connection,
|
||||
cm: ConnectionManager<T>,
|
||||
tx: mpsc::UnboundedSender<Data>,
|
||||
rx: mpsc::UnboundedReceiver<Data>,
|
||||
close: bool,
|
||||
conn_id: i32,
|
||||
#[cfg(windows)]
|
||||
file_transfer_enabled: bool,
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref CLIENTS: RwLock<HashMap<i32,Client>> = Default::default();
|
||||
static ref CLIENTS: RwLock<HashMap<i32, Client>> = Default::default();
|
||||
static ref CLICK_TIME: AtomicI64 = AtomicI64::new(0);
|
||||
}
|
||||
|
||||
@ -215,23 +231,185 @@ pub fn get_clients_length() -> usize {
|
||||
clients.len()
|
||||
}
|
||||
|
||||
pub enum ClipboardFileData {
|
||||
impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
||||
#[cfg(windows)]
|
||||
Clip((i32, ipc::ClipbaordFile)),
|
||||
Enable((i32, bool)),
|
||||
async fn enable_cliprdr_file_context(&mut self, conn_id: i32, enabled: bool) {
|
||||
if conn_id == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let pre_enabled = ContextSend::is_enabled();
|
||||
ContextSend::enable(enabled);
|
||||
if !pre_enabled && ContextSend::is_enabled() {
|
||||
allow_err!(
|
||||
self.stream
|
||||
.send(&Data::ClipbaordFile(clipboard::ClipbaordFile::MonitorReady))
|
||||
.await
|
||||
);
|
||||
}
|
||||
clipboard::set_conn_enabled(conn_id, enabled);
|
||||
if !enabled {
|
||||
ContextSend::proc(|context: &mut Box<CliprdrClientContext>| -> u32 {
|
||||
clipboard::empty_clipboard(context, conn_id);
|
||||
0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn run(&mut self) {
|
||||
use hbb_common::config::LocalConfig;
|
||||
|
||||
// for tmp use, without real conn id
|
||||
let mut write_jobs: Vec<fs::TransferJob> = Vec::new();
|
||||
|
||||
#[cfg(windows)]
|
||||
if self.conn_id > 0 {
|
||||
self.enable_cliprdr_file_context(self.conn_id, self.file_transfer_enabled)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
let rx_clip1;
|
||||
let mut rx_clip;
|
||||
let _tx_clip;
|
||||
#[cfg(windows)]
|
||||
if self.conn_id > 0 {
|
||||
rx_clip1 = clipboard::get_rx_cliprdr_server(self.conn_id);
|
||||
rx_clip = rx_clip1.lock().await;
|
||||
} else {
|
||||
let rx_clip2;
|
||||
(_tx_clip, rx_clip2) = unbounded_channel::<clipboard::ClipbaordFile>();
|
||||
rx_clip1 = Arc::new(TokioMutex::new(rx_clip2));
|
||||
rx_clip = rx_clip1.lock().await;
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
(_tx_clip, rx_clip) = unbounded_channel::<i32>();
|
||||
}
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
res = self.stream.next() => {
|
||||
match res {
|
||||
Err(err) => {
|
||||
log::info!("cm ipc connection closed: {}", err);
|
||||
break;
|
||||
}
|
||||
Ok(Some(data)) => {
|
||||
match data {
|
||||
Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording} => {
|
||||
log::debug!("conn_id: {}", id);
|
||||
self.cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, self.tx.clone());
|
||||
self.conn_id = id;
|
||||
#[cfg(windows)]
|
||||
{
|
||||
self.file_transfer_enabled = _file_transfer_enabled;
|
||||
}
|
||||
break;
|
||||
}
|
||||
Data::Close => {
|
||||
#[cfg(windows)]
|
||||
self.enable_cliprdr_file_context(self.conn_id, false).await;
|
||||
log::info!("cm ipc connection closed from connection request");
|
||||
break;
|
||||
}
|
||||
Data::Disconnected => {
|
||||
self.close = false;
|
||||
#[cfg(windows)]
|
||||
self.enable_cliprdr_file_context(self.conn_id, false).await;
|
||||
log::info!("cm ipc connection disconnect");
|
||||
break;
|
||||
}
|
||||
Data::PrivacyModeState((_id, _)) => {
|
||||
#[cfg(windows)]
|
||||
cm_inner_send(_id, data);
|
||||
}
|
||||
Data::ClickTime(ms) => {
|
||||
CLICK_TIME.store(ms, Ordering::SeqCst);
|
||||
}
|
||||
Data::ChatMessage { text } => {
|
||||
self.cm.new_message(self.conn_id, text);
|
||||
}
|
||||
Data::FS(fs) => {
|
||||
handle_fs(fs, &mut write_jobs, &self.tx).await;
|
||||
}
|
||||
Data::ClipbaordFile(_clip) => {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let conn_id = self.conn_id;
|
||||
ContextSend::proc(|context: &mut Box<CliprdrClientContext>| -> u32 {
|
||||
clipboard::server_clip_file(context, conn_id, _clip)
|
||||
});
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
Data::ClipboardFileEnabled(_enabled) => {
|
||||
#[cfg(windows)]
|
||||
self.enable_cliprdr_file_context(self.conn_id, _enabled).await;
|
||||
}
|
||||
Data::Theme(dark) => {
|
||||
self.cm.change_theme(dark);
|
||||
}
|
||||
Data::Language(lang) => {
|
||||
LocalConfig::set_option("lang".to_owned(), lang);
|
||||
self.cm.change_language();
|
||||
}
|
||||
_ => {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Some(data) = self.rx.recv() => {
|
||||
if self.stream.send(&data).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
clip_file = rx_clip.recv() => match clip_file {
|
||||
Some(_clip) => {
|
||||
#[cfg(windows)]
|
||||
allow_err!(self.tx.send(Data::ClipbaordFile(_clip)));
|
||||
}
|
||||
None => {
|
||||
//
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn ipc_task(stream: Connection, cm: ConnectionManager<T>) {
|
||||
log::debug!("ipc task begin");
|
||||
let (tx, rx) = mpsc::unbounded_channel::<Data>();
|
||||
let mut task_runner = Self {
|
||||
stream,
|
||||
cm,
|
||||
tx,
|
||||
rx,
|
||||
close: true,
|
||||
conn_id: 0,
|
||||
#[cfg(windows)]
|
||||
file_transfer_enabled: false,
|
||||
};
|
||||
|
||||
task_runner.run().await;
|
||||
if task_runner.conn_id > 0 {
|
||||
task_runner.run().await;
|
||||
}
|
||||
if task_runner.conn_id > 0 {
|
||||
task_runner
|
||||
.cm
|
||||
.remove_connection(task_runner.conn_id, task_runner.close);
|
||||
}
|
||||
log::debug!("ipc task end");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) {
|
||||
use hbb_common::config::LocalConfig;
|
||||
|
||||
let (tx_file, _rx_file) = mpsc::unbounded_channel::<ClipboardFileData>();
|
||||
#[cfg(windows)]
|
||||
let cm_clip = cm.clone();
|
||||
#[cfg(windows)]
|
||||
std::thread::spawn(move || start_clipboard_file(cm_clip, _rx_file));
|
||||
|
||||
#[cfg(windows)]
|
||||
std::thread::spawn(move || {
|
||||
log::info!("try create privacy mode window");
|
||||
@ -253,94 +431,10 @@ pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) {
|
||||
match result {
|
||||
Ok(stream) => {
|
||||
log::debug!("Got new connection");
|
||||
let mut stream = Connection::new(stream);
|
||||
let cm = cm.clone();
|
||||
let tx_file = tx_file.clone();
|
||||
tokio::spawn(async move {
|
||||
// for tmp use, without real conn id
|
||||
let conn_id_tmp = -1;
|
||||
let mut conn_id: i32 = 0;
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<Data>();
|
||||
let mut write_jobs: Vec<fs::TransferJob> = Vec::new();
|
||||
let mut close = true;
|
||||
loop {
|
||||
tokio::select! {
|
||||
res = stream.next() => {
|
||||
match res {
|
||||
Err(err) => {
|
||||
log::info!("cm ipc connection closed: {}", err);
|
||||
break;
|
||||
}
|
||||
Ok(Some(data)) => {
|
||||
match data {
|
||||
Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled, restart, recording} => {
|
||||
log::debug!("conn_id: {}", id);
|
||||
conn_id = id;
|
||||
tx_file.send(ClipboardFileData::Enable((id, file_transfer_enabled))).ok();
|
||||
cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, tx.clone());
|
||||
}
|
||||
Data::Close => {
|
||||
tx_file.send(ClipboardFileData::Enable((conn_id, false))).ok();
|
||||
log::info!("cm ipc connection closed from connection request");
|
||||
break;
|
||||
}
|
||||
Data::Disconnected => {
|
||||
close = false;
|
||||
tx_file.send(ClipboardFileData::Enable((conn_id, false))).ok();
|
||||
log::info!("cm ipc connection disconnect");
|
||||
break;
|
||||
}
|
||||
Data::PrivacyModeState((id, _)) => {
|
||||
conn_id = conn_id_tmp;
|
||||
allow_err!(tx.send(data));
|
||||
}
|
||||
Data::ClickTime(ms) => {
|
||||
CLICK_TIME.store(ms, Ordering::SeqCst);
|
||||
}
|
||||
Data::ChatMessage { text } => {
|
||||
cm.new_message(conn_id, text);
|
||||
}
|
||||
Data::FS(fs) => {
|
||||
handle_fs(fs, &mut write_jobs, &tx).await;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
Data::ClipbaordFile(_clip) => {
|
||||
tx_file
|
||||
.send(ClipboardFileData::Clip((conn_id, _clip)))
|
||||
.ok();
|
||||
}
|
||||
#[cfg(windows)]
|
||||
Data::ClipboardFileEnabled(enabled) => {
|
||||
tx_file
|
||||
.send(ClipboardFileData::Enable((conn_id, enabled)))
|
||||
.ok();
|
||||
}
|
||||
Data::Theme(dark) => {
|
||||
cm.change_theme(dark);
|
||||
}
|
||||
Data::Language(lang) => {
|
||||
LocalConfig::set_option("lang".to_owned(), lang);
|
||||
cm.change_language();
|
||||
}
|
||||
_ => {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Some(data) = rx.recv() => {
|
||||
if stream.send(&data).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if conn_id != conn_id_tmp {
|
||||
cm.remove_connection(conn_id, close);
|
||||
}
|
||||
});
|
||||
tokio::spawn(IpcTaskRunner::<T>::ipc_task(
|
||||
Connection::new(stream),
|
||||
cm.clone(),
|
||||
));
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Couldn't get cm client: {:?}", err);
|
||||
@ -642,66 +736,7 @@ fn send_raw(msg: Message, tx: &UnboundedSender<Data>) {
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
pub async fn start_clipboard_file<T: InvokeUiCM>(
|
||||
cm: ConnectionManager<T>,
|
||||
mut rx: mpsc::UnboundedReceiver<ClipboardFileData>,
|
||||
) {
|
||||
let mut cliprdr_context = None;
|
||||
let mut rx_clip_client = clipboard::get_rx_clip_client().lock().await;
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
clip_file = rx_clip_client.recv() => match clip_file {
|
||||
Some((conn_id, clip)) => {
|
||||
cmd_inner_send(
|
||||
conn_id,
|
||||
Data::ClipbaordFile(clip)
|
||||
);
|
||||
}
|
||||
None => {
|
||||
//
|
||||
}
|
||||
},
|
||||
server_msg = rx.recv() => match server_msg {
|
||||
Some(ClipboardFileData::Clip((conn_id, clip))) => {
|
||||
if let Some(ctx) = cliprdr_context.as_mut() {
|
||||
clipboard::server_clip_file(ctx, conn_id, clip);
|
||||
}
|
||||
}
|
||||
Some(ClipboardFileData::Enable((id, enabled))) => {
|
||||
if enabled && cliprdr_context.is_none() {
|
||||
cliprdr_context = Some(match clipboard::create_cliprdr_context(true, false) {
|
||||
Ok(context) => {
|
||||
log::info!("clipboard context for file transfer created.");
|
||||
context
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!(
|
||||
"Create clipboard context for file transfer: {}",
|
||||
err.to_string()
|
||||
);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
clipboard::set_conn_enabled(id, enabled);
|
||||
if !enabled {
|
||||
if let Some(ctx) = cliprdr_context.as_mut() {
|
||||
clipboard::empty_clipboard(ctx, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn cmd_inner_send(id: i32, data: Data) {
|
||||
fn cm_inner_send(id: i32, data: Data) {
|
||||
let lock = CLIENTS.read().unwrap();
|
||||
if id != 0 {
|
||||
if let Some(s) = lock.get(&id) {
|
||||
|
@ -76,11 +76,11 @@ pub fn goto_install() {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn install_me(_options: String, _path: String, silent: bool, debug: bool) {
|
||||
pub fn install_me(_options: String, _path: String, _silent: bool, _debug: bool) {
|
||||
#[cfg(windows)]
|
||||
std::thread::spawn(move || {
|
||||
allow_err!(crate::platform::windows::install_me(
|
||||
&_options, _path, silent, debug
|
||||
&_options, _path, _silent, _debug
|
||||
));
|
||||
std::process::exit(0);
|
||||
});
|
||||
|
@ -6,6 +6,7 @@ use crate::client::{
|
||||
load_config, send_mouse, start_video_audio_threads, FileManager, Key, LoginConfigHandler,
|
||||
QualityStatus, KEY_MAP, SERVER_KEYBOARD_ENABLED,
|
||||
};
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::common::IS_X11;
|
||||
use crate::{client::Data, client::Interface};
|
||||
use async_trait::async_trait;
|
||||
@ -812,6 +813,7 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
let keycode: u32 = keycode as u32;
|
||||
let scancode: u32 = scancode as u32;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let key = rdev::key_from_scancode(scancode) as RdevKey;
|
||||
// Windows requires special handling
|
||||
#[cfg(target_os = "windows")]
|
||||
|
Loading…
Reference in New Issue
Block a user