From 200fc56a4ac787d74475f434ebb20558f46708c9 Mon Sep 17 00:00:00 2001 From: dignow Date: Sun, 6 Aug 2023 14:00:48 +0800 Subject: [PATCH 01/37] tmp commit Signed-off-by: dignow --- flutter/lib/models/input_model.dart | 74 +++++++++++++++++----------- libs/hbb_common/protos/message.proto | 20 ++++++++ 2 files changed, 64 insertions(+), 30 deletions(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 6b50aa37..eeaa1a44 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -592,17 +592,52 @@ class InputModel { return; } evt['type'] = type; + + final pos = handlePointerDevicePos( + x, + y, + isMove, + type, + onExit: onExit, + buttons: evt['buttons'], + ); + if (pos == null) { + return; + } + evt['x'] = '${pos.x}}'; + evt['y'] = '${pos.y}'; + + Map mapButtons = { + kPrimaryMouseButton: 'left', + kSecondaryMouseButton: 'right', + kMiddleMouseButton: 'wheel', + kBackMouseButton: 'back', + kForwardMouseButton: 'forward' + }; + evt['buttons'] = mapButtons[evt['buttons']] ?? ''; + + bind.sessionSendMouse(sessionId: sessionId, msg: json.encode(evt)); + } + + Point? handlePointerDevicePos( + double x, + double y, + bool isMove, + String evtType, { + bool onExit = false, + int buttons = kPrimaryMouseButton, + }) { y -= CanvasModel.topToEdge; x -= CanvasModel.leftToEdge; final canvasModel = parent.target!.canvasModel; - final nearThr = 3; - var nearRight = (canvasModel.size.width - x) < nearThr; - var nearBottom = (canvasModel.size.height - y) < nearThr; - final ffiModel = parent.target!.ffiModel; if (isMove) { canvasModel.moveDesktopMouse(x, y); } + + final nearThr = 3; + var nearRight = (canvasModel.size.width - x) < nearThr; + var nearBottom = (canvasModel.size.height - y) < nearThr; final d = ffiModel.display; final imageWidth = d.width * canvasModel.scale; final imageHeight = d.height * canvasModel.scale; @@ -650,7 +685,7 @@ class InputModel { } catch (e) { debugPrintStack( label: 'canvasModel.scale value ${canvasModel.scale}, $e'); - return; + return null; } int minX = d.x.toInt(); @@ -661,38 +696,17 @@ class InputModel { evtY = trySetNearestRange(evtY, minY, maxY, 5); if (evtX < minX || evtY < minY || evtX > maxX || evtY > maxY) { // If left mouse up, no early return. - if (evt['buttons'] != kPrimaryMouseButton || type != 'up') { - return; + if (buttons != kPrimaryMouseButton || evtType != 'up') { + return null; } } - if (type != '') { + if (evtType != '') { evtX = 0; evtY = 0; } - evt['x'] = '$evtX'; - evt['y'] = '$evtY'; - var buttons = ''; - switch (evt['buttons']) { - case kPrimaryMouseButton: - buttons = 'left'; - break; - case kSecondaryMouseButton: - buttons = 'right'; - break; - case kMiddleMouseButton: - buttons = 'wheel'; - break; - case kBackMouseButton: - buttons = 'back'; - break; - case kForwardMouseButton: - buttons = 'forward'; - break; - } - evt['buttons'] = buttons; - bind.sessionSendMouse(sessionId: sessionId, msg: json.encode(evt)); + return Point(evtX, evtY); } /// Web only diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index e6862bc8..82206cbf 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -118,9 +118,29 @@ message TouchScaleUpdate { int32 scale = 1; } +message TouchPanStart { + int32 x = 1; + int32 y = 2; +} + +message TouchPanUpdate { + // The delta x position relative to the previous position. + int32 x = 1; + // The delta y position relative to the previous position. + int32 y = 2; +} + +message TouchPanEnd { + int32 x = 1; + int32 y = 2; +} + message TouchEvent { oneof union { TouchScaleUpdate scale_update = 1; + TouchPanStart pan_start = 2; + TouchPanUpdate pan_update = 3; + TouchPanEnd pan_end = 4; } } From 8999bbf2974722ec60d9732b303f322613b4341e Mon Sep 17 00:00:00 2001 From: dignow Date: Tue, 8 Aug 2023 23:53:53 +0800 Subject: [PATCH 02/37] tmp commit Signed-off-by: dignow --- flutter/lib/models/input_model.dart | 71 +++++++++++++++++------------ 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index eeaa1a44..e8cd92a9 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -223,14 +223,8 @@ class InputModel { command: command); } - Map getEvent(PointerEvent evt, String type) { + Map _getMouseEvent(PointerEvent evt, String type) { final Map out = {}; - out['x'] = evt.position.dx; - out['y'] = evt.position.dy; - if (alt) out['alt'] = 'true'; - if (shift) out['shift'] = 'true'; - if (ctrl) out['ctrl'] = 'true'; - if (command) out['command'] = 'true'; // Check update event type and set buttons to be sent. int buttons = _lastButtons; @@ -260,7 +254,6 @@ class InputModel { out['buttons'] = buttons; out['type'] = type; - return out; } @@ -292,7 +285,7 @@ class InputModel { } /// Modify the given modifier map [evt] based on current modifier key status. - Map modify(Map evt) { + Map modify(Map evt) { if (ctrl) evt['ctrl'] = 'true'; if (shift) evt['shift'] = 'true'; if (alt) evt['alt'] = 'true'; @@ -334,13 +327,14 @@ class InputModel { isPhysicalMouse.value = true; } if (isPhysicalMouse.value) { - handleMouse(getEvent(e, _kMouseEventMove)); + handleMouse(_getMouseEvent(e, _kMouseEventMove), e.position); } } void onPointerPanZoomStart(PointerPanZoomStartEvent e) { _lastScale = 1.0; _stopFling = true; + handlePointerEvent('touch', 'pan_start', e); } // https://docs.flutter.dev/release/breaking-changes/trackpad-gestures @@ -465,21 +459,21 @@ class InputModel { } } if (isPhysicalMouse.value) { - handleMouse(getEvent(e, _kMouseEventDown)); + handleMouse(_getMouseEvent(e, _kMouseEventDown), e.position); } } void onPointUpImage(PointerUpEvent e) { if (e.kind != ui.PointerDeviceKind.mouse) return; if (isPhysicalMouse.value) { - handleMouse(getEvent(e, _kMouseEventUp)); + handleMouse(_getMouseEvent(e, _kMouseEventUp), e.position); } } void onPointMoveImage(PointerMoveEvent e) { if (e.kind != ui.PointerDeviceKind.mouse) return; if (isPhysicalMouse.value) { - handleMouse(getEvent(e, _kMouseEventMove)); + handleMouse(_getMouseEvent(e, _kMouseEventMove), e.position); } } @@ -504,19 +498,16 @@ class InputModel { } void refreshMousePos() => handleMouse({ - 'x': lastMousePos.dx, - 'y': lastMousePos.dy, 'buttons': 0, 'type': _kMouseEventMove, - }); + }, lastMousePos); void tryMoveEdgeOnExit(Offset pos) => handleMouse( { - 'x': pos.dx, - 'y': pos.dy, 'buttons': 0, 'type': _kMouseEventMove, }, + pos, onExit: true, ); @@ -550,17 +541,27 @@ class InputModel { return Offset(x, y); } - void handleMouse( - Map evt, { - bool onExit = false, - }) { - double x = evt['x']; - double y = max(0.0, evt['y']); - final cursorModel = parent.target!.cursorModel; + void handlePointerEvent(String kind, String type, PointerEvent e) { + if (checkPeerControlProtected(e.position.dx, max(0.0, e.position.dy))) { + return; + } + // Only touch events are handled for now. So we can just ignore buttons. + // to-do: handle mouse events + bind.sessionSendPointer( + sessionId: sessionId, + msg: json.encode({ + modify({ + kind: {type: e.position.toString()} + }) + }), + ); + } + bool checkPeerControlProtected(double x, double y) { + final cursorModel = parent.target!.cursorModel; if (cursorModel.isPeerControlProtected) { lastMousePos = ui.Offset(x, y); - return; + return true; } if (!cursorModel.gotMouseControl) { @@ -571,10 +572,23 @@ class InputModel { cursorModel.gotMouseControl = true; } else { lastMousePos = ui.Offset(x, y); - return; + return true; } } lastMousePos = ui.Offset(x, y); + return false; + } + + void handleMouse( + Map evt, + Offset offset, { + bool onExit = false, + }) { + double x = offset.dx; + double y = max(0.0, offset.dy); + if (checkPeerControlProtected(x, y)) { + return; + } var type = ''; var isMove = false; @@ -615,8 +629,7 @@ class InputModel { kForwardMouseButton: 'forward' }; evt['buttons'] = mapButtons[evt['buttons']] ?? ''; - - bind.sessionSendMouse(sessionId: sessionId, msg: json.encode(evt)); + bind.sessionSendMouse(sessionId: sessionId, msg: json.encode(modify(evt))); } Point? handlePointerDevicePos( From 933c99110ccc3e607e70f2ba97211266b5fcf5eb Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 9 Aug 2023 07:39:16 +0800 Subject: [PATCH 03/37] tmp commit Signed-off-by: dignow --- flutter/lib/models/input_model.dart | 85 ++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index e8cd92a9..ef1a90d6 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -62,11 +62,11 @@ class InputModel { int _lastButtons = 0; Offset lastMousePos = Offset.zero; - get id => parent.target?.id ?? ""; - late final SessionID sessionId; bool get keyboardPerm => parent.target!.ffiModel.keyboard; + String get id => parent.target?.id ?? ''; + String? get peerPlatform => parent.target?.ffiModel.pi.platform; InputModel(this.parent) { sessionId = parent.target!.sessionId; @@ -334,21 +334,26 @@ class InputModel { void onPointerPanZoomStart(PointerPanZoomStartEvent e) { _lastScale = 1.0; _stopFling = true; - handlePointerEvent('touch', 'pan_start', e); + + if (peerPlatform == kPeerPlatformAndroid) { + handlePointerEvent('touch', 'pan_start', e.position); + } } // https://docs.flutter.dev/release/breaking-changes/trackpad-gestures void onPointerPanZoomUpdate(PointerPanZoomUpdateEvent e) { - final scale = ((e.scale - _lastScale) * 1000).toInt(); - _lastScale = e.scale; + if (peerPlatform != kPeerPlatformAndroid) { + final scale = ((e.scale - _lastScale) * 1000).toInt(); + _lastScale = e.scale; - if (scale != 0) { - bind.sessionSendPointer( - sessionId: sessionId, - msg: json.encode({ - 'touch': {'scale': scale} - })); - return; + if (scale != 0) { + bind.sessionSendPointer( + sessionId: sessionId, + msg: json.encode({ + 'touch': {'scale': scale} + })); + return; + } } final delta = e.panDelta; @@ -356,7 +361,8 @@ class InputModel { var x = delta.dx.toInt(); var y = delta.dy.toInt(); - if (parent.target?.ffiModel.pi.platform == kPeerPlatformLinux) { + if (peerPlatform == kPeerPlatformLinux || + peerPlatform == kPeerPlatformAndroid) { _trackpadScrollUnsent += (delta * _trackpadSpeed); x = _trackpadScrollUnsent.dx.truncate(); y = _trackpadScrollUnsent.dy.truncate(); @@ -372,9 +378,13 @@ class InputModel { } } if (x != 0 || y != 0) { - bind.sessionSendMouse( - sessionId: sessionId, - msg: '{"type": "trackpad", "x": "$x", "y": "$y"}'); + if (peerPlatform == kPeerPlatformAndroid) { + handlePointerEvent('touch', 'pan_move', e.delta); + } else { + bind.sessionSendMouse( + sessionId: sessionId, + msg: '{"type": "trackpad", "x": "$x", "y": "$y"}'); + } } } @@ -430,6 +440,11 @@ class InputModel { } void onPointerPanZoomEnd(PointerPanZoomEndEvent e) { + if (peerPlatform == kPeerPlatformAndroid) { + handlePointerEvent('touch', 'pan_end', e.position); + return; + } + bind.sessionSendPointer( sessionId: sessionId, msg: json.encode({ @@ -541,23 +556,39 @@ class InputModel { return Offset(x, y); } - void handlePointerEvent(String kind, String type, PointerEvent e) { - if (checkPeerControlProtected(e.position.dx, max(0.0, e.position.dy))) { + void handlePointerEvent(String kind, String type, Offset offset) { + double x = offset.dx; + double y = max(0.0, offset.dy); + if (_checkPeerControlProtected(x, y)) { return; } // Only touch events are handled for now. So we can just ignore buttons. // to-do: handle mouse events - bind.sessionSendPointer( - sessionId: sessionId, - msg: json.encode({ - modify({ - kind: {type: e.position.toString()} - }) - }), + + final isMoveTypes = ['pan', 'pan_start', 'pan_end']; + final pos = handlePointerDevicePos( + x, + y, + isMoveTypes.contains(type), + type, ); + if (pos == null) { + return; + } + + final evt = { + kind: { + type: { + 'x': '${pos.x}', + 'y': '${pos.y}', + } + } + }; + bind.sessionSendPointer( + sessionId: sessionId, msg: json.encode({modify(evt)})); } - bool checkPeerControlProtected(double x, double y) { + bool _checkPeerControlProtected(double x, double y) { final cursorModel = parent.target!.cursorModel; if (cursorModel.isPeerControlProtected) { lastMousePos = ui.Offset(x, y); @@ -586,7 +617,7 @@ class InputModel { }) { double x = offset.dx; double y = max(0.0, offset.dy); - if (checkPeerControlProtected(x, y)) { + if (_checkPeerControlProtected(x, y)) { return; } From d6f1abad959e21e4dbdc4cb05c1a976083b8bb2c Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 9 Aug 2023 09:00:23 +0800 Subject: [PATCH 04/37] tmp commit Signed-off-by: dignow --- .../com/carriez/flutter_hbb/InputService.kt | 31 ++++++++++++++ .../com/carriez/flutter_hbb/MainService.kt | 17 ++++++-- libs/scrap/src/android/ffi.rs | 6 +-- src/server/connection.rs | 42 +++++++++++++++++-- 4 files changed, 85 insertions(+), 11 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt index 905a2734..8e14a590 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt @@ -26,6 +26,13 @@ const val WHEEL_BUTTON_UP = 34 const val WHEEL_DOWN = 523331 const val WHEEL_UP = 963 +const val TOUCH_SCALE_START = 1 +const val TOUCH_SCALE = 2 +const val TOUCH_SCALE_END = 3 +const val TOUCH_PAN_START = 4 +const val TOUCH_PAN_UPDATE = 5 +const val TOUCH_PAN_END = 6 + const val WHEEL_STEP = 120 const val WHEEL_DURATION = 50L const val LONG_TAP_DELAY = 200L @@ -167,6 +174,30 @@ class InputService : AccessibilityService() { } } + @RequiresApi(Build.VERSION_CODES.N) + fun onTouchInput(mask: Int, _x: Int, _y: Int) { + val x = max(0, _x) + val y = max(0, _y) + when (mask) { + TOUCH_PAN_UPDATE -> { + mouseX += x * SCREEN_INFO.scale + mouseY += y * SCREEN_INFO.scale + continueGesture(mouseX, mouseY) + } + TOUCH_PAN_START -> { + mouseX = x * SCREEN_INFO.scale + mouseY = y * SCREEN_INFO.scale + startGesture(mouseX, mouseY) + } + TOUCH_PAN_END -> { + mouseX = x * SCREEN_INFO.scale + mouseY = y * SCREEN_INFO.scale + endGesture(mouseX, mouseY) + } + else -> {} + } + } + @RequiresApi(Build.VERSION_CODES.N) private fun consumeWheelActions() { if (isWheelActionsPolling) { diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index 78e4e451..f3220370 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -71,17 +71,26 @@ class MainService : Service() { @Keep @RequiresApi(Build.VERSION_CODES.N) - fun rustMouseInput(mask: Int, x: Int, y: Int) { + fun rustPointerInput(kind: String, mask: Int, x: Int, y: Int) { // turn on screen with LIFT_DOWN when screen off - if (!powerManager.isInteractive && mask == LIFT_DOWN) { + if (!powerManager.isInteractive && (kind == "touch" || mask == LIFT_DOWN)) { if (wakeLock.isHeld) { - Log.d(logTag,"Turn on Screen, WakeLock release") + Log.d(logTag, "Turn on Screen, WakeLock release") wakeLock.release() } Log.d(logTag,"Turn on Screen") wakeLock.acquire(5000) } else { - InputService.ctx?.onMouseInput(mask,x,y) + when (name) { + "touch" -> { + InputService.ctx?.onTouchInput(mask, x, y) + } + "mouse" -> { + InputService.ctx?.onMouseInput(mask, x, y) + } + else -> { + } + } } } diff --git a/libs/scrap/src/android/ffi.rs b/libs/scrap/src/android/ffi.rs index 6855fd3f..3801bbc8 100644 --- a/libs/scrap/src/android/ffi.rs +++ b/libs/scrap/src/android/ffi.rs @@ -154,7 +154,7 @@ pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_init( } } -pub fn call_main_service_mouse_input(mask: i32, x: i32, y: i32) -> JniResult<()> { +pub fn call_main_service_pointer_input(kind: String, mask: i32, x: i32, y: i32) -> JniResult<()> { if let (Some(jvm), Some(ctx)) = ( JVM.read().unwrap().as_ref(), MAIN_SERVICE_CTX.read().unwrap().as_ref(), @@ -162,9 +162,9 @@ pub fn call_main_service_mouse_input(mask: i32, x: i32, y: i32) -> JniResult<()> let mut env = jvm.attach_current_thread_as_daemon()?; env.call_method( ctx, - "rustMouseInput", + "rustPointerInput", "(III)V", - &[JValue::Int(mask), JValue::Int(x), JValue::Int(y)], + &[JValue(kind), JValue::Int(mask), JValue::Int(x), JValue::Int(y)], )?; return Ok(()); } else { diff --git a/src/server/connection.rs b/src/server/connection.rs index e32a4c1c..913c831d 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1546,8 +1546,10 @@ impl Connection { match msg.union { Some(message::Union::MouseEvent(me)) => { #[cfg(any(target_os = "android", target_os = "ios"))] - if let Err(e) = call_main_service_mouse_input(me.mask, me.x, me.y) { - log::debug!("call_main_service_mouse_input fail:{}", e); + if let Err(e) = + call_main_service_pointer_input("mouse".to_string(), me.mask, me.x, me.y) + { + log::debug!("call_main_service_pointer_input fail:{}", e); } #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.peer_keyboard_enabled() { @@ -1559,8 +1561,40 @@ impl Connection { self.input_mouse(me, self.inner.id()); } } - Some(message::Union::PointerDeviceEvent(pde)) => - { + Some(message::Union::PointerDeviceEvent(pde)) => { + #[cfg(any(target_os = "android", target_os = "ios"))] + if let Err(e) = match pde.union { + Some(pointer_device_event::Union::TouchEvent(touch)) => match touch.union { + Some(touch_event::Union::PanStart(pan_start)) => { + call_main_service_pointer_input( + "touch".to_string(), + 4, + pan_start.x, + pan_start.y, + ) + } + Some(touch_event::Union::PanUpdate(pan_start)) => { + call_main_service_pointer_input( + "touch".to_string(), + 5, + pan_start.x, + pan_start.y, + ) + } + Some(touch_event::Union::PanEnd(pan_start)) => { + call_main_service_pointer_input( + "touch".to_string(), + 6, + pan_start.x, + pan_start.y, + ) + } + _ => Ok(()), + }, + _ => Ok(()), + } { + log::debug!("call_main_service_pointer_input fail:{}", e); + } #[cfg(not(any(target_os = "android", target_os = "ios")))] if self.peer_keyboard_enabled() { MOUSE_MOVE_TIME.store(get_time(), Ordering::SeqCst); From 06ee68f836a4e5635ec41fc460afe39d5e851f41 Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 9 Aug 2023 22:26:13 +0800 Subject: [PATCH 05/37] tmp commit Signed-off-by: dignow --- flutter/lib/models/input_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index ef1a90d6..18e9d0d2 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -649,7 +649,7 @@ class InputModel { if (pos == null) { return; } - evt['x'] = '${pos.x}}'; + evt['x'] = '${pos.x}'; evt['y'] = '${pos.y}'; Map mapButtons = { From 93a600a0a8d86cfdb632f15552cb0b887448e0fc Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 9 Aug 2023 23:42:53 +0800 Subject: [PATCH 06/37] tmp commit Signed-off-by: dignow --- flutter/lib/common/widgets/remote_input.dart | 12 +-- flutter/lib/consts.dart | 3 + flutter/lib/models/input_model.dart | 101 ++++++++++++------- src/flutter_ffi.rs | 41 ++++++-- src/server/connection.rs | 12 +-- src/ui_session_interface.rs | 43 ++++++++ 6 files changed, 155 insertions(+), 57 deletions(-) diff --git a/flutter/lib/common/widgets/remote_input.dart b/flutter/lib/common/widgets/remote_input.dart index 35eaf8a7..b00cd1fb 100644 --- a/flutter/lib/common/widgets/remote_input.dart +++ b/flutter/lib/common/widgets/remote_input.dart @@ -6,6 +6,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/common.dart'; +import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/models/model.dart'; import 'package:flutter_hbb/models/input_model.dart'; @@ -263,9 +264,9 @@ class _RawTouchGestureDetectorRegionState if (scale != 0) { bind.sessionSendPointer( sessionId: sessionId, - msg: json.encode({ - 'touch': {'scale': scale} - })); + msg: json.encode( + PointerEventToRust(kPointerEventKindTouch, 'scale', scale) + .toJson())); } } else { // mobile @@ -283,9 +284,8 @@ class _RawTouchGestureDetectorRegionState if (isDesktop) { bind.sessionSendPointer( sessionId: sessionId, - msg: json.encode({ - 'touch': {'scale': 0} - })); + msg: json.encode( + PointerEventToRust(kPointerEventKindTouch, 'scale', 0).toJson())); } else { // mobile _scale = 1; diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 5376196e..f26e83e0 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -54,6 +54,9 @@ const String kTabLabelSettingPage = "Settings"; const String kWindowPrefix = "wm_"; const int kWindowMainId = 0; +const String kPointerEventKindTouch = "touch"; +const String kPointerEventKindMouse = "mouse"; + // the executable name of the portable version const String kEnvPortableExecutable = "RUSTDESK_APPNAME"; diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 18e9d0d2..9b1ec443 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -35,6 +35,24 @@ extension ToString on MouseButtons { } } +class PointerEventToRust { + final String kind; + final String type; + final dynamic value; + + PointerEventToRust(this.kind, this.type, this.value); + + Map toJson() { + return { + 'k': kind, + 'v': { + 't': type, + 'v': value, + } + }; + } +} + class InputModel { final WeakReference parent; String keyboardMode = "legacy"; @@ -349,9 +367,9 @@ class InputModel { if (scale != 0) { bind.sessionSendPointer( sessionId: sessionId, - msg: json.encode({ - 'touch': {'scale': scale} - })); + msg: json.encode( + PointerEventToRust(kPointerEventKindTouch, 'scale', scale) + .toJson())); return; } } @@ -379,7 +397,7 @@ class InputModel { } if (x != 0 || y != 0) { if (peerPlatform == kPeerPlatformAndroid) { - handlePointerEvent('touch', 'pan_move', e.delta); + handlePointerEvent('touch', 'pan_update', Offset(x.toDouble(), y.toDouble())); } else { bind.sessionSendMouse( sessionId: sessionId, @@ -447,9 +465,8 @@ class InputModel { bind.sessionSendPointer( sessionId: sessionId, - msg: json.encode({ - 'touch': {'scale': 0} - })); + msg: json.encode( + PointerEventToRust(kPointerEventKindTouch, 'scale', 0).toJson())); waitLastFlingDone(); _stopFling = false; @@ -558,34 +575,40 @@ class InputModel { void handlePointerEvent(String kind, String type, Offset offset) { double x = offset.dx; - double y = max(0.0, offset.dy); + double y = offset.dy; if (_checkPeerControlProtected(x, y)) { return; } // Only touch events are handled for now. So we can just ignore buttons. // to-do: handle mouse events - final isMoveTypes = ['pan', 'pan_start', 'pan_end']; - final pos = handlePointerDevicePos( - x, - y, - isMoveTypes.contains(type), - type, - ); - if (pos == null) { - return; + late final dynamic evtValue; + if (type == 'pan_update') { + evtValue = { + 'x': '${x.toInt()}', + 'y': '${y.toInt()}', + }; + } else { + final isMoveTypes = ['pan_start', 'pan_end']; + final pos = handlePointerDevicePos( + kPointerEventKindTouch, + x, + y, + isMoveTypes.contains(type), + type, + ); + if (pos == null) { + return; + } + evtValue = { + 'x': '${pos.x}', + 'y': '${pos.y}', + }; } - final evt = { - kind: { - type: { - 'x': '${pos.x}', - 'y': '${pos.y}', - } - } - }; + final evt = PointerEventToRust(kind, type, evtValue).toJson(); bind.sessionSendPointer( - sessionId: sessionId, msg: json.encode({modify(evt)})); + sessionId: sessionId, msg: json.encode(modify(evt))); } bool _checkPeerControlProtected(double x, double y) { @@ -639,6 +662,7 @@ class InputModel { evt['type'] = type; final pos = handlePointerDevicePos( + kPointerEventKindMouse, x, y, isMove, @@ -649,8 +673,13 @@ class InputModel { if (pos == null) { return; } - evt['x'] = '${pos.x}'; - evt['y'] = '${pos.y}'; + if (type != '') { + evt['x'] = 0; + evt['y'] = 0; + } else { + evt['x'] = '${pos.x}'; + evt['y'] = '${pos.y}'; + } Map mapButtons = { kPrimaryMouseButton: 'left', @@ -664,6 +693,7 @@ class InputModel { } Point? handlePointerDevicePos( + String kind, double x, double y, bool isMove, @@ -738,18 +768,15 @@ class InputModel { int maxY = (d.y + d.height).toInt() - 1; evtX = trySetNearestRange(evtX, minX, maxX, 5); evtY = trySetNearestRange(evtY, minY, maxY, 5); - if (evtX < minX || evtY < minY || evtX > maxX || evtY > maxY) { - // If left mouse up, no early return. - if (buttons != kPrimaryMouseButton || evtType != 'up') { - return null; + if (kind == kPointerEventKindMouse) { + if (evtX < minX || evtY < minY || evtX > maxX || evtY > maxY) { + // If left mouse up, no early return. + if (!(buttons == kPrimaryMouseButton && evtType == 'up')) { + return null; + } } } - if (evtType != '') { - evtX = 0; - evtY = 0; - } - return Point(evtX, evtY); } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index ef4c9e2b..cf5d2339 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1177,14 +1177,39 @@ pub fn session_send_pointer(session_id: SessionID, msg: String) { let ctrl = m.get("ctrl").is_some(); let shift = m.get("shift").is_some(); let command = m.get("command").is_some(); - if let Some(touch_event) = m.get("touch") { - if let Some(scale) = touch_event.get("scale") { - if let Some(session) = SESSIONS.read().unwrap().get(&session_id) { - if let Some(scale) = scale.as_i64() { - session.send_touch_scale(scale as _, alt, ctrl, shift, command); - } - } - } + match (m.get("k"), m.get("v")) { + (Some(k), Some(v)) => match k.as_str() { + Some("touch") => match v.as_str() { + Some("scale") => match v.get("v") { + Some(scale) => { + if let Some(scale) = scale.as_i64() { + if let Some(session) = SESSIONS.read().unwrap().get(&session_id) { + session.send_touch_scale(scale as _, alt, ctrl, shift, command); + } + } + } + None => {} + }, + Some(pan_event) => match (v.get("x"), v.get("y")) { + (Some(x), Some(y)) => { + if let Some(x) = x.as_i64() { + if let Some(y) = y.as_i64() { + if let Some(session) = SESSIONS.read().unwrap().get(&session_id) + { + session.send_touch_pan_event( + pan_event, x as _, y as _, alt, ctrl, shift, command, + ); + } + } + } + } + _ => {} + }, + _ => {} + }, + _ => {} + }, + _ => {} } } } diff --git a/src/server/connection.rs b/src/server/connection.rs index 913c831d..972750fc 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1573,20 +1573,20 @@ impl Connection { pan_start.y, ) } - Some(touch_event::Union::PanUpdate(pan_start)) => { + Some(touch_event::Union::PanUpdate(pan_update)) => { call_main_service_pointer_input( "touch".to_string(), 5, - pan_start.x, - pan_start.y, + pan_update.x, + pan_update.y, ) } - Some(touch_event::Union::PanEnd(pan_start)) => { + Some(touch_event::Union::PanEnd(pan_end)) => { call_main_service_pointer_input( "touch".to_string(), 6, - pan_start.x, - pan_start.y, + pan_end.x, + pan_end.y, ) } _ => Ok(()), diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 7ffcaac6..23035a55 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -724,6 +724,49 @@ impl Session { send_pointer_device_event(evt, alt, ctrl, shift, command, self); } + pub fn send_touch_pan_event( + &self, + event: &str, + x: i32, + y: i32, + alt: bool, + ctrl: bool, + shift: bool, + command: bool, + ) { + let mut touch_evt = TouchEvent::new(); + match event { + "pan_start" => { + touch_evt.set_pan_start(TouchPanStart { + x, + y, + ..Default::default() + }); + } + "pan_update" => { + touch_evt.set_pan_update(TouchPanUpdate { + x, + y, + ..Default::default() + }); + } + "pan_end" => { + touch_evt.set_pan_end(TouchPanEnd { + x, + y, + ..Default::default() + }); + } + _ => { + log::warn!("unknown touch pan event: {}", event); + return; + } + }; + let mut evt = PointerDeviceEvent::new(); + evt.set_touch_event(touch_evt); + send_pointer_device_event(evt, alt, ctrl, shift, command, self); + } + pub fn send_mouse( &self, mask: i32, From 9e0feb0b64c14c97eccea3905923dbcc7acbaab5 Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 10 Aug 2023 01:02:27 +0800 Subject: [PATCH 07/37] tmp debug Signed-off-by: dignow --- flutter/lib/models/input_model.dart | 8 +++---- src/flutter_ffi.rs | 33 ++++++++++++++--------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 9b1ec443..5f851c54 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -585,8 +585,8 @@ class InputModel { late final dynamic evtValue; if (type == 'pan_update') { evtValue = { - 'x': '${x.toInt()}', - 'y': '${y.toInt()}', + 'x': x.toInt(), + 'y': y.toInt(), }; } else { final isMoveTypes = ['pan_start', 'pan_end']; @@ -601,8 +601,8 @@ class InputModel { return; } evtValue = { - 'x': '${pos.x}', - 'y': '${pos.y}', + 'x': pos.x, + 'y': pos.y, }; } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index cf5d2339..c6f8ca77 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1179,30 +1179,29 @@ pub fn session_send_pointer(session_id: SessionID, msg: String) { let command = m.get("command").is_some(); match (m.get("k"), m.get("v")) { (Some(k), Some(v)) => match k.as_str() { - Some("touch") => match v.as_str() { - Some("scale") => match v.get("v") { + Some("touch") => match v.get("t").and_then(|t| t.as_str()) { + Some("scale") => match v.get("v").and_then(|s| s.as_i64()) { Some(scale) => { - if let Some(scale) = scale.as_i64() { - if let Some(session) = SESSIONS.read().unwrap().get(&session_id) { - session.send_touch_scale(scale as _, alt, ctrl, shift, command); - } + if let Some(session) = SESSIONS.read().unwrap().get(&session_id) { + session.send_touch_scale(scale as _, alt, ctrl, shift, command); } } None => {} }, - Some(pan_event) => match (v.get("x"), v.get("y")) { - (Some(x), Some(y)) => { - if let Some(x) = x.as_i64() { - if let Some(y) = y.as_i64() { - if let Some(session) = SESSIONS.read().unwrap().get(&session_id) - { - session.send_touch_pan_event( - pan_event, x as _, y as _, alt, ctrl, shift, command, - ); - } + Some(pan_event) => match v.get("v") { + Some(v) => match ( + v.get("x").and_then(|x| x.as_i64()), + v.get("y").and_then(|y| y.as_i64()), + ) { + (Some(x), Some(y)) => { + if let Some(session) = SESSIONS.read().unwrap().get(&session_id) { + session.send_touch_pan_event( + pan_event, x as _, y as _, alt, ctrl, shift, command, + ); } } - } + _ => {} + }, _ => {} }, _ => {} From da16a799fa6851289bc18b081cfa4e715e6163f3 Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 10 Aug 2023 01:10:59 +0800 Subject: [PATCH 08/37] fix build Signed-off-by: dignow --- libs/scrap/src/android/ffi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/scrap/src/android/ffi.rs b/libs/scrap/src/android/ffi.rs index 3801bbc8..78908a2b 100644 --- a/libs/scrap/src/android/ffi.rs +++ b/libs/scrap/src/android/ffi.rs @@ -164,7 +164,7 @@ pub fn call_main_service_pointer_input(kind: String, mask: i32, x: i32, y: i32) ctx, "rustPointerInput", "(III)V", - &[JValue(kind), JValue::Int(mask), JValue::Int(x), JValue::Int(y)], + &[JValue::String(kind), JValue::Int(mask), JValue::Int(x), JValue::Int(y)], )?; return Ok(()); } else { From 9476d7fdbb7edb3b3ebcaa8326622918b71af227 Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 10 Aug 2023 01:24:39 +0800 Subject: [PATCH 09/37] try fix build Signed-off-by: dignow --- libs/scrap/src/android/ffi.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/scrap/src/android/ffi.rs b/libs/scrap/src/android/ffi.rs index 78908a2b..ba775dfd 100644 --- a/libs/scrap/src/android/ffi.rs +++ b/libs/scrap/src/android/ffi.rs @@ -163,8 +163,8 @@ pub fn call_main_service_pointer_input(kind: String, mask: i32, x: i32, y: i32) env.call_method( ctx, "rustPointerInput", - "(III)V", - &[JValue::String(kind), JValue::Int(mask), JValue::Int(x), JValue::Int(y)], + "(Ljava/lang/String;III)V", + &[JValue::Object(&JObject::from(name)), JValue::Int(mask), JValue::Int(x), JValue::Int(y)], )?; return Ok(()); } else { From e89ae475f6a4d03ca79d0382e71d1fb9ea816b40 Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 10 Aug 2023 03:01:46 +0800 Subject: [PATCH 10/37] fix build Signed-off-by: dignow --- .../main/kotlin/com/carriez/flutter_hbb/MainService.kt | 2 +- libs/scrap/src/android/ffi.rs | 5 +++-- src/server/connection.rs | 10 +++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt index f3220370..535a3f8c 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt @@ -81,7 +81,7 @@ class MainService : Service() { Log.d(logTag,"Turn on Screen") wakeLock.acquire(5000) } else { - when (name) { + when (kind) { "touch" -> { InputService.ctx?.onTouchInput(mask, x, y) } diff --git a/libs/scrap/src/android/ffi.rs b/libs/scrap/src/android/ffi.rs index ba775dfd..e9c60ef9 100644 --- a/libs/scrap/src/android/ffi.rs +++ b/libs/scrap/src/android/ffi.rs @@ -154,17 +154,18 @@ pub extern "system" fn Java_com_carriez_flutter_1hbb_MainService_init( } } -pub fn call_main_service_pointer_input(kind: String, mask: i32, x: i32, y: i32) -> JniResult<()> { +pub fn call_main_service_pointer_input(kind: &str, mask: i32, x: i32, y: i32) -> JniResult<()> { if let (Some(jvm), Some(ctx)) = ( JVM.read().unwrap().as_ref(), MAIN_SERVICE_CTX.read().unwrap().as_ref(), ) { let mut env = jvm.attach_current_thread_as_daemon()?; + let kind = env.new_string(kind)?; env.call_method( ctx, "rustPointerInput", "(Ljava/lang/String;III)V", - &[JValue::Object(&JObject::from(name)), JValue::Int(mask), JValue::Int(x), JValue::Int(y)], + &[JValue::Object(&JObject::from(kind)), JValue::Int(mask), JValue::Int(x), JValue::Int(y)], )?; return Ok(()); } else { diff --git a/src/server/connection.rs b/src/server/connection.rs index 972750fc..f107d15a 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -39,7 +39,7 @@ use hbb_common::{ tokio_util::codec::{BytesCodec, Framed}, }; #[cfg(any(target_os = "android", target_os = "ios"))] -use scrap::android::call_main_service_mouse_input; +use scrap::android::call_main_service_pointer_input; use serde_json::{json, value::Value}; use sha2::{Digest, Sha256}; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -1547,7 +1547,7 @@ impl Connection { Some(message::Union::MouseEvent(me)) => { #[cfg(any(target_os = "android", target_os = "ios"))] if let Err(e) = - call_main_service_pointer_input("mouse".to_string(), me.mask, me.x, me.y) + call_main_service_pointer_input("mouse", me.mask, me.x, me.y) { log::debug!("call_main_service_pointer_input fail:{}", e); } @@ -1567,7 +1567,7 @@ impl Connection { Some(pointer_device_event::Union::TouchEvent(touch)) => match touch.union { Some(touch_event::Union::PanStart(pan_start)) => { call_main_service_pointer_input( - "touch".to_string(), + "touch", 4, pan_start.x, pan_start.y, @@ -1575,7 +1575,7 @@ impl Connection { } Some(touch_event::Union::PanUpdate(pan_update)) => { call_main_service_pointer_input( - "touch".to_string(), + "touch", 5, pan_update.x, pan_update.y, @@ -1583,7 +1583,7 @@ impl Connection { } Some(touch_event::Union::PanEnd(pan_end)) => { call_main_service_pointer_input( - "touch".to_string(), + "touch", 6, pan_end.x, pan_end.y, From b9c8df70196ca0eeb9f8115729a3775b1cd38600 Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 10 Aug 2023 03:55:03 +0800 Subject: [PATCH 11/37] debug Signed-off-by: dignow --- flutter/lib/models/input_model.dart | 7 +++++-- src/server/connection.rs | 11 ++--------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index 5f851c54..eaff4ed6 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -638,6 +638,7 @@ class InputModel { Offset offset, { bool onExit = false, }) { + debugPrint('REMOVE ME ========================= 111'); double x = offset.dx; double y = max(0.0, offset.dy); if (_checkPeerControlProtected(x, y)) { @@ -671,11 +672,12 @@ class InputModel { buttons: evt['buttons'], ); if (pos == null) { + debugPrint('REMOVE ME ========================= 222'); return; } if (type != '') { - evt['x'] = 0; - evt['y'] = 0; + evt['x'] = '0'; + evt['y'] = '0'; } else { evt['x'] = '${pos.x}'; evt['y'] = '${pos.y}'; @@ -689,6 +691,7 @@ class InputModel { kForwardMouseButton: 'forward' }; evt['buttons'] = mapButtons[evt['buttons']] ?? ''; + debugPrint('REMOVE ME ========================= 333 $evt'); bind.sessionSendMouse(sessionId: sessionId, msg: json.encode(modify(evt))); } diff --git a/src/server/connection.rs b/src/server/connection.rs index f107d15a..ef886449 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1546,9 +1546,7 @@ impl Connection { match msg.union { Some(message::Union::MouseEvent(me)) => { #[cfg(any(target_os = "android", target_os = "ios"))] - if let Err(e) = - call_main_service_pointer_input("mouse", me.mask, me.x, me.y) - { + if let Err(e) = call_main_service_pointer_input("mouse", me.mask, me.x, me.y) { log::debug!("call_main_service_pointer_input fail:{}", e); } #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -1582,12 +1580,7 @@ impl Connection { ) } Some(touch_event::Union::PanEnd(pan_end)) => { - call_main_service_pointer_input( - "touch", - 6, - pan_end.x, - pan_end.y, - ) + call_main_service_pointer_input("touch", 6, pan_end.x, pan_end.y) } _ => Ok(()), }, From be982d95ea548baeee37d805e198b67a2b97da8a Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 10 Aug 2023 08:37:18 +0800 Subject: [PATCH 12/37] tmp build Signed-off-by: dignow --- .../src/main/kotlin/com/carriez/flutter_hbb/InputService.kt | 2 ++ flutter/lib/models/input_model.dart | 6 +----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt index 8e14a590..84ef3179 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt @@ -190,8 +190,10 @@ class InputService : AccessibilityService() { startGesture(mouseX, mouseY) } TOUCH_PAN_END -> { + endGesture(mouseX, mouseY) mouseX = x * SCREEN_INFO.scale mouseY = y * SCREEN_INFO.scale + continueGesture(mouseX, mouseY) endGesture(mouseX, mouseY) } else -> {} diff --git a/flutter/lib/models/input_model.dart b/flutter/lib/models/input_model.dart index eaff4ed6..971bbb7e 100644 --- a/flutter/lib/models/input_model.dart +++ b/flutter/lib/models/input_model.dart @@ -379,8 +379,7 @@ class InputModel { var x = delta.dx.toInt(); var y = delta.dy.toInt(); - if (peerPlatform == kPeerPlatformLinux || - peerPlatform == kPeerPlatformAndroid) { + if (peerPlatform == kPeerPlatformLinux) { _trackpadScrollUnsent += (delta * _trackpadSpeed); x = _trackpadScrollUnsent.dx.truncate(); y = _trackpadScrollUnsent.dy.truncate(); @@ -638,7 +637,6 @@ class InputModel { Offset offset, { bool onExit = false, }) { - debugPrint('REMOVE ME ========================= 111'); double x = offset.dx; double y = max(0.0, offset.dy); if (_checkPeerControlProtected(x, y)) { @@ -672,7 +670,6 @@ class InputModel { buttons: evt['buttons'], ); if (pos == null) { - debugPrint('REMOVE ME ========================= 222'); return; } if (type != '') { @@ -691,7 +688,6 @@ class InputModel { kForwardMouseButton: 'forward' }; evt['buttons'] = mapButtons[evt['buttons']] ?? ''; - debugPrint('REMOVE ME ========================= 333 $evt'); bind.sessionSendMouse(sessionId: sessionId, msg: json.encode(modify(evt))); } From 5f7055e28286174440f7a8ee17ff7bec0d600af2 Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 10 Aug 2023 13:57:57 +0800 Subject: [PATCH 13/37] debug Signed-off-by: dignow --- .../src/main/kotlin/com/carriez/flutter_hbb/InputService.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt index 84ef3179..65b7931d 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt @@ -180,8 +180,8 @@ class InputService : AccessibilityService() { val y = max(0, _y) when (mask) { TOUCH_PAN_UPDATE -> { - mouseX += x * SCREEN_INFO.scale - mouseY += y * SCREEN_INFO.scale + mouseX -= x * SCREEN_INFO.scale + mouseY -= y * SCREEN_INFO.scale continueGesture(mouseX, mouseY) } TOUCH_PAN_START -> { @@ -193,8 +193,6 @@ class InputService : AccessibilityService() { endGesture(mouseX, mouseY) mouseX = x * SCREEN_INFO.scale mouseY = y * SCREEN_INFO.scale - continueGesture(mouseX, mouseY) - endGesture(mouseX, mouseY) } else -> {} } From 072430cef5c0452b4a1ea36b243b1eddc1d0fbdd Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 10 Aug 2023 14:48:00 +0800 Subject: [PATCH 14/37] debug android scroll Signed-off-by: dignow --- .../kotlin/com/carriez/flutter_hbb/InputService.kt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt index 65b7931d..55c57729 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt @@ -176,23 +176,21 @@ class InputService : AccessibilityService() { @RequiresApi(Build.VERSION_CODES.N) fun onTouchInput(mask: Int, _x: Int, _y: Int) { - val x = max(0, _x) - val y = max(0, _y) when (mask) { TOUCH_PAN_UPDATE -> { - mouseX -= x * SCREEN_INFO.scale - mouseY -= y * SCREEN_INFO.scale + mouseX -= _x * SCREEN_INFO.scale + mouseY -= _y * SCREEN_INFO.scale continueGesture(mouseX, mouseY) } TOUCH_PAN_START -> { - mouseX = x * SCREEN_INFO.scale - mouseY = y * SCREEN_INFO.scale + mouseX = max(0, _x) * SCREEN_INFO.scale + mouseY = max(0, _y) * SCREEN_INFO.scale startGesture(mouseX, mouseY) } TOUCH_PAN_END -> { endGesture(mouseX, mouseY) - mouseX = x * SCREEN_INFO.scale - mouseY = y * SCREEN_INFO.scale + mouseX = max(0, _x) * SCREEN_INFO.scale + mouseY = max(0, _y) * SCREEN_INFO.scale } else -> {} } From 5b2358c97fb40978b30473b034a913c7d6137cce Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 10 Aug 2023 14:49:49 +0800 Subject: [PATCH 15/37] debug android scroll Signed-off-by: dignow --- .../app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt index 55c57729..20355896 100644 --- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt +++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/InputService.kt @@ -180,6 +180,8 @@ class InputService : AccessibilityService() { TOUCH_PAN_UPDATE -> { mouseX -= _x * SCREEN_INFO.scale mouseY -= _y * SCREEN_INFO.scale + mouseX = max(0, mouseX); + mouseY = max(0, mouseY); continueGesture(mouseX, mouseY) } TOUCH_PAN_START -> { From 6368ab691c47f6292e2156c2f3734fb46f45fa6e Mon Sep 17 00:00:00 2001 From: dignow Date: Thu, 10 Aug 2023 16:08:30 +0800 Subject: [PATCH 16/37] simple refactor, move code from flutter_ffi.rs to flutter.rs Signed-off-by: dignow --- src/flutter.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++ src/flutter_ffi.rs | 40 +---------------------- 2 files changed, 80 insertions(+), 39 deletions(-) diff --git a/src/flutter.rs b/src/flutter.rs index 52190ce2..32be6b1c 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1130,6 +1130,85 @@ pub fn stop_global_event_stream(app_type: String) { let _ = GLOBAL_EVENT_STREAM.write().unwrap().remove(&app_type); } +#[inline] +fn session_send_touch_scale( + session_id: SessionID, + v: &serde_json::Value, + alt: bool, + ctrl: bool, + shift: bool, + command: bool, +) { + match v.get("v").and_then(|s| s.as_i64()) { + Some(scale) => { + if let Some(session) = SESSIONS.read().unwrap().get(&session_id) { + session.send_touch_scale(scale as _, alt, ctrl, shift, command); + } + } + None => {} + } +} + +#[inline] +fn session_send_touch_pan( + session_id: SessionID, + v: &serde_json::Value, + pan_event: &str, + alt: bool, + ctrl: bool, + shift: bool, + command: bool, +) { + match v.get("v") { + Some(v) => match ( + v.get("x").and_then(|x| x.as_i64()), + v.get("y").and_then(|y| y.as_i64()), + ) { + (Some(x), Some(y)) => { + if let Some(session) = SESSIONS.read().unwrap().get(&session_id) { + session + .send_touch_pan_event(pan_event, x as _, y as _, alt, ctrl, shift, command); + } + } + _ => {} + }, + _ => {} + } +} + +fn session_send_touch_event( + session_id: SessionID, + v: &serde_json::Value, + alt: bool, + ctrl: bool, + shift: bool, + command: bool, +) { + match v.get("t").and_then(|t| t.as_str()) { + Some("scale") => session_send_touch_scale(session_id, v, alt, ctrl, shift, command), + Some(pan_event) => { + session_send_touch_pan(session_id, v, pan_event, alt, ctrl, shift, command) + } + _ => {} + } +} + +pub fn session_send_pointer(session_id: SessionID, msg: String) { + if let Ok(m) = serde_json::from_str::>(&msg) { + let alt = m.get("alt").is_some(); + let ctrl = m.get("ctrl").is_some(); + let shift = m.get("shift").is_some(); + let command = m.get("command").is_some(); + match (m.get("k"), m.get("v")) { + (Some(k), Some(v)) => match k.as_str() { + Some("touch") => session_send_touch_event(session_id, v, alt, ctrl, shift, command), + _ => {} + }, + _ => {} + } + } +} + #[no_mangle] unsafe extern "C" fn get_rgba() {} diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index c6f8ca77..34a1b61e 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1172,45 +1172,7 @@ pub fn main_clear_ab() { } pub fn session_send_pointer(session_id: SessionID, msg: String) { - if let Ok(m) = serde_json::from_str::>(&msg) { - let alt = m.get("alt").is_some(); - let ctrl = m.get("ctrl").is_some(); - let shift = m.get("shift").is_some(); - let command = m.get("command").is_some(); - match (m.get("k"), m.get("v")) { - (Some(k), Some(v)) => match k.as_str() { - Some("touch") => match v.get("t").and_then(|t| t.as_str()) { - Some("scale") => match v.get("v").and_then(|s| s.as_i64()) { - Some(scale) => { - if let Some(session) = SESSIONS.read().unwrap().get(&session_id) { - session.send_touch_scale(scale as _, alt, ctrl, shift, command); - } - } - None => {} - }, - Some(pan_event) => match v.get("v") { - Some(v) => match ( - v.get("x").and_then(|x| x.as_i64()), - v.get("y").and_then(|y| y.as_i64()), - ) { - (Some(x), Some(y)) => { - if let Some(session) = SESSIONS.read().unwrap().get(&session_id) { - session.send_touch_pan_event( - pan_event, x as _, y as _, alt, ctrl, shift, command, - ); - } - } - _ => {} - }, - _ => {} - }, - _ => {} - }, - _ => {} - }, - _ => {} - } - } + super::flutter::session_send_pointer(session_id, msg); } pub fn session_send_mouse(session_id: SessionID, msg: String) { From e205577145d52e2174a7bdc76d12b30123fa5e18 Mon Sep 17 00:00:00 2001 From: dignow Date: Mon, 14 Aug 2023 18:28:31 +0800 Subject: [PATCH 17/37] refact, tab to window, flutter data, init commit Signed-off-by: dignow --- flutter/lib/consts.dart | 2 + .../lib/desktop/pages/remote_tab_page.dart | 34 ++++++++- flutter/lib/models/model.dart | 50 +++++++++++++- flutter/lib/utils/multi_window_manager.dart | 69 ++++++++++++------- 4 files changed, 127 insertions(+), 28 deletions(-) diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index b3ec3aa9..0621bc80 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -5,6 +5,7 @@ import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/models/state_model.dart'; const double kDesktopRemoteTabBarHeight = 28.0; +const int kInvalidWindowId = -1; const int kMainWindowId = 0; const String kPeerPlatformWindows = "Windows"; @@ -39,6 +40,7 @@ const String kWindowEventGetSessionIdList = "get_session_id_list"; const String kWindowEventMoveTabToNewWindow = "move_tab_to_new_window"; const String kWindowEventCloseForSeparateWindow = "close_for_separate_window"; +const String kWindowEventSendNewWindowData = "send_new_window_data"; const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs"; const String kOptionOpenInTabs = "allow-open-in-tabs"; diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 3762a2b5..01f4e5ca 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:flutter_hbb/consts.dart'; +import 'package:flutter_hbb/models/model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/desktop/pages/remote_page.dart'; import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart'; @@ -148,9 +149,40 @@ class _ConnectionTabPageState extends State { .toList() .join(';'); } else if (call.method == kWindowEventCloseForSeparateWindow) { - final peerId = call.arguments; + debugPrint('REMOVE ME ============================= ${call.arguments}'); + final peerId = call.arguments['peerId']; + final newWindowId = call.arguments['newWindowId']; + late RemotePage page; + try { + page = tabController.state.value.tabs.firstWhere((tab) { + return tab.key == peerId; + }).page as RemotePage; + } catch (e) { + debugPrint('Failed to find tab for peerId $peerId'); + return false; + } + final sendRes = await rustDeskWinManager.call( + newWindowId, + kWindowEventSendNewWindowData, + page.ffi.ffiModel.cachedPeerData) as bool; + if (!sendRes) { + return false; + } + // Pass the required data to new window. closeSessionOnDispose[peerId] = false; tabController.closeBy(peerId); + return true; + } else if (call.method == kWindowEventSendNewWindowData) { + if (peerId == null) { + return false; + } + if (tabController.state.value.tabs.isEmpty) { + return false; + } + final page = tabController.state.value.tabs[0].page as RemotePage; + page.ffi.ffiModel + .handleCachedPeerData(call.arguments as CachedPeerData, peerId!); + return true; } _update_remote_count(); }); diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 4dc17f9c..286e89a3 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -41,7 +41,19 @@ final _waitForImageDialogShow = {}; final _waitForFirstImage = {}; final _constSessionId = Uuid().v4obj(); +class CachedPeerData { + Map updatePrivacyMode = {}; + Map peerInfo = {}; + List> cursorDataList = []; + Map lastCursorId = {}; + bool secure = false; + bool direct = false; + + CachedPeerData(); +} + class FfiModel with ChangeNotifier { + CachedPeerData cachedPeerData = CachedPeerData(); PeerInfo _pi = PeerInfo(); Display _display = Display(); @@ -117,6 +129,8 @@ class FfiModel with ChangeNotifier { } setConnectionType(String peerId, bool secure, bool direct) { + cachedPeerData.secure = secure; + cachedPeerData.direct = direct; _secure = secure; _direct = direct; try { @@ -143,6 +157,22 @@ class FfiModel with ChangeNotifier { _permissions.clear(); } + handleCachedPeerData(CachedPeerData data, String peerId) async { + handleMsgBox({ + 'type': 'success', + 'title': 'Successful', + 'text': 'Connected, waiting for image...', + 'link': '', + }, sessionId, peerId); + updatePrivacyMode(data.updatePrivacyMode, sessionId, peerId); + setConnectionType(peerId, data.secure, data.direct); + handlePeerInfo(data.peerInfo, peerId); + for (var element in data.cursorDataList) { + handleCursorData(element); + } + handleCursorId(data.lastCursorId); + } + // todo: why called by two position StreamEventHandler startEventListener(SessionID sessionId, String peerId) { return (evt) async { @@ -159,9 +189,9 @@ class FfiModel with ChangeNotifier { } else if (name == 'switch_display') { handleSwitchDisplay(evt, sessionId, peerId); } else if (name == 'cursor_data') { - await parent.target?.cursorModel.updateCursorData(evt); + await handleCursorData(evt); } else if (name == 'cursor_id') { - await parent.target?.cursorModel.updateCursorId(evt); + await handleCursorId(evt); } else if (name == 'cursor_position') { await parent.target?.cursorModel.updateCursorPosition(evt, peerId); } else if (name == 'clipboard') { @@ -453,6 +483,8 @@ class FfiModel with ChangeNotifier { /// Handle the peer info event based on [evt]. handlePeerInfo(Map evt, String peerId) async { + cachedPeerData.peerInfo = evt; + // recent peer updated by handle_peer_info(ui_session_interface.rs) --> handle_peer_info(client.rs) --> save_config(client.rs) bind.mainLoadRecentPeers(); @@ -568,9 +600,20 @@ class FfiModel with ChangeNotifier { return d; } + handleCursorId(Map evt) async { + cachedPeerData.lastCursorId = evt; + await parent.target?.cursorModel.updateCursorId(evt); + } + + handleCursorData(Map evt) async { + cachedPeerData.cursorDataList.add(evt); + await parent.target?.cursorModel.updateCursorData(evt); + } + /// Handle the peer info synchronization event based on [evt]. handleSyncPeerInfo(Map evt, SessionID sessionId) async { if (evt['displays'] != null) { + cachedPeerData.peerInfo['displays'] = evt['displays']; List displays = json.decode(evt['displays']); List newDisplays = []; for (int i = 0; i < displays.length; ++i) { @@ -1667,7 +1710,8 @@ class FFI { stream.listen((message) { if (closed) return; if (isSessionAdded && !isToNewWindowNotified.value) { - bind.sessionReadyToNewWindow(sessionId: sessionId); + // bind.sessionReadyToNewWindow(sessionId: sessionId); + bind.sessionRefresh(sessionId: sessionId); isToNewWindowNotified.value = true; } () async { diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 3f7d995b..4230182d 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -28,6 +28,13 @@ extension Index on int { } } +class MultiWindowCallResult { + int windowId; + dynamic result; + + MultiWindowCallResult(this.windowId, this.result); +} + /// Window Manager /// mainly use it in `Main Window` /// use it in sub window is not recommended @@ -49,7 +56,10 @@ class RustDeskMultiWindowManager { 'id': peerId, 'session_id': sessionId, }; - await _newSession( + // It's better to use the window id that returned by _newSession. + // Do not pass original window id to _newSession, + // as this function cann't promise the necessary data is passed to new window. + final multiWindowRes = await _newSession( false, WindowType.RemoteDesktop, kWindowEventNewRemoteDesktop, @@ -57,17 +67,21 @@ class RustDeskMultiWindowManager { _remoteDesktopWindows, jsonEncode(params), ); + // kWindowEventCloseForSeparateWindow will not only close the tab, but also pass the required data to new window. await DesktopMultiWindow.invokeMethod( - windowId, kWindowEventCloseForSeparateWindow, peerId); + windowId, kWindowEventCloseForSeparateWindow, { + 'peerId': peerId, + 'newWindowId': multiWindowRes.windowId, + }); } - newSessionWindow( + Future newSessionWindow( WindowType type, String remoteId, String msg, List windows) async { final windowController = await DesktopMultiWindow.createWindow(msg); + final windowId = windowController.windowId; windowController - ..setFrame(const Offset(0, 0) & - Size(1280 + windowController.windowId * 20, - 720 + windowController.windowId * 20)) + ..setFrame( + const Offset(0, 0) & Size(1280 + windowId * 20, 720 + windowId * 20)) ..center() ..setTitle(getWindowNameWithId( remoteId, @@ -76,11 +90,12 @@ class RustDeskMultiWindowManager { if (Platform.isMacOS) { Future.microtask(() => windowController.show()); } - registerActiveWindow(windowController.windowId); - windows.add(windowController.windowId); + registerActiveWindow(windowId); + windows.add(windowId); + return windowId; } - _newSession( + Future _newSession( bool openInTabs, WindowType type, String methodName, @@ -90,9 +105,10 @@ class RustDeskMultiWindowManager { ) async { if (openInTabs) { if (windows.isEmpty) { - await newSessionWindow(type, remoteId, msg, windows); + final windowId = await newSessionWindow(type, remoteId, msg, windows); + return MultiWindowCallResult(windowId, null); } else { - call(type, methodName, msg); + return call(type, methodName, msg); } } else { if (_inactiveWindows.isNotEmpty) { @@ -103,15 +119,16 @@ class RustDeskMultiWindowManager { await DesktopMultiWindow.invokeMethod(windowId, methodName, msg); WindowController.fromWindowId(windowId).show(); registerActiveWindow(windowId); - return; + return MultiWindowCallResult(windowId, null); } } } - await newSessionWindow(type, remoteId, msg, windows); + final windowId = await newSessionWindow(type, remoteId, msg, windows); + return MultiWindowCallResult(windowId, null); } } - Future newSession( + Future newSession( WindowType type, String methodName, String remoteId, @@ -143,15 +160,15 @@ class RustDeskMultiWindowManager { for (final windowId in windows) { if (await DesktopMultiWindow.invokeMethod( windowId, kWindowEventActiveSession, remoteId)) { - return; + return MultiWindowCallResult(windowId, null); } } } - await _newSession(openInTabs, type, methodName, remoteId, windows, msg); + return _newSession(openInTabs, type, methodName, remoteId, windows, msg); } - Future newRemoteDesktop( + Future newRemoteDesktop( String remoteId, { String? password, String? switchUuid, @@ -168,7 +185,7 @@ class RustDeskMultiWindowManager { ); } - Future newFileTransfer(String remoteId, + Future newFileTransfer(String remoteId, {String? password, bool? forceRelay}) async { return await newSession( WindowType.FileTransfer, @@ -180,7 +197,7 @@ class RustDeskMultiWindowManager { ); } - Future newPortForward(String remoteId, bool isRDP, + Future newPortForward(String remoteId, bool isRDP, {String? password, bool? forceRelay}) async { return await newSession( WindowType.PortForward, @@ -193,18 +210,22 @@ class RustDeskMultiWindowManager { ); } - Future call(WindowType type, String methodName, dynamic args) async { + Future call( + WindowType type, String methodName, dynamic args) async { final wnds = _findWindowsByType(type); if (wnds.isEmpty) { - return; + return MultiWindowCallResult(kInvalidWindowId, null); } for (final windowId in wnds) { if (_activeWindows.contains(windowId)) { - return await DesktopMultiWindow.invokeMethod( - windowId, methodName, args); + final res = + await DesktopMultiWindow.invokeMethod(windowId, methodName, args); + return MultiWindowCallResult(windowId, res); } } - return await DesktopMultiWindow.invokeMethod(wnds[0], methodName, args); + final res = + await DesktopMultiWindow.invokeMethod(wnds[0], methodName, args); + return MultiWindowCallResult(wnds[0], res); } List _findWindowsByType(WindowType type) { From fad88c27189a0cd9f5b93f19240991e66e95cc5f Mon Sep 17 00:00:00 2001 From: dignow Date: Mon, 14 Aug 2023 20:40:58 +0800 Subject: [PATCH 18/37] refact, tab to window, remove rust cache data Signed-off-by: dignow --- flutter/lib/consts.dart | 3 +- .../desktop/pages/desktop_setting_page.dart | 1 - flutter/lib/desktop/pages/remote_page.dart | 5 +- .../lib/desktop/pages/remote_tab_page.dart | 49 +++++---------- .../lib/desktop/widgets/remote_toolbar.dart | 2 +- .../lib/models/desktop_render_texture.dart | 8 +-- flutter/lib/models/model.dart | 63 ++++++++++++++++--- flutter/lib/utils/multi_window_manager.dart | 12 +--- src/client.rs | 2 +- src/client/io_loop.rs | 49 +-------------- src/flutter.rs | 8 ++- src/flutter_ffi.rs | 8 --- src/port_forward.rs | 2 +- src/ui_cm_interface.rs | 1 + src/ui_session_interface.rs | 43 ++----------- 15 files changed, 100 insertions(+), 156 deletions(-) diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 0621bc80..25c3e16a 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -39,8 +39,7 @@ const String kWindowEventGetRemoteList = "get_remote_list"; const String kWindowEventGetSessionIdList = "get_session_id_list"; const String kWindowEventMoveTabToNewWindow = "move_tab_to_new_window"; -const String kWindowEventCloseForSeparateWindow = "close_for_separate_window"; -const String kWindowEventSendNewWindowData = "send_new_window_data"; +const String kWindowEventGetCachedSessionData = "get_cached_session_data"; const String kOptionOpenNewConnInTabs = "enable-open-new-connections-in-tabs"; const String kOptionOpenInTabs = "allow-open-in-tabs"; diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index b868042a..8d5ddb7d 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -17,7 +17,6 @@ import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart'; -import 'package:window_manager/window_manager.dart'; import '../../common/widgets/dialog.dart'; import '../../common/widgets/login.dart'; diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 28212e4c..2daffacc 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -35,6 +35,7 @@ class RemotePage extends StatefulWidget { Key? key, required this.id, required this.sessionId, + required this.tabWindowId, required this.password, required this.toolbarState, required this.tabController, @@ -44,6 +45,7 @@ class RemotePage extends StatefulWidget { final String id; final SessionID? sessionId; + final int? tabWindowId; final String? password; final ToolbarState toolbarState; final String? switchUuid; @@ -106,6 +108,7 @@ class _RemotePageState extends State password: widget.password, switchUuid: widget.switchUuid, forceRelay: widget.forceRelay, + tabWindowId: widget.tabWindowId, ); WidgetsBinding.instance.addPostFrameCallback((_) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); @@ -204,7 +207,7 @@ class _RemotePageState extends State // https://github.com/flutter/flutter/issues/64935 super.dispose(); debugPrint("REMOTE PAGE dispose session $sessionId ${widget.id}"); - await _renderTexture.destroy(); + await _renderTexture.destroy(widget.tabWindowId != null); // ensure we leave this session, this is a double check bind.sessionEnterOrLeave(sessionId: sessionId, enter: false); DesktopMultiWindow.removeListener(this); diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 01f4e5ca..51bcec51 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -7,7 +7,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:flutter_hbb/consts.dart'; -import 'package:flutter_hbb/models/model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/desktop/pages/remote_page.dart'; import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart'; @@ -56,6 +55,7 @@ class _ConnectionTabPageState extends State { RemoteCountState.init(); peerId = params['id']; final sessionId = params['session_id']; + final tabWindowId = params['tab_window_id']; if (peerId != null) { ConnectionTypeState.init(peerId!); tabController.onSelected = (id) { @@ -78,6 +78,7 @@ class _ConnectionTabPageState extends State { key: ValueKey(peerId), id: peerId!, sessionId: sessionId == null ? null : SessionID(sessionId), + tabWindowId: tabWindowId, password: params['password'], toolbarState: _toolbarState, tabController: tabController, @@ -99,12 +100,14 @@ class _ConnectionTabPageState extends State { print( "[Remote Page] call ${call.method} with args ${call.arguments} from window $fromWindowId"); + dynamic returnValue; // for simplify, just replace connectionId if (call.method == kWindowEventNewRemoteDesktop) { final args = jsonDecode(call.arguments); final id = args['id']; final switchUuid = args['switch_uuid']; final sessionId = args['session_id']; + final tabWindowId = args['tab_window_id']; windowOnTop(windowId()); ConnectionTypeState.init(id); _toolbarState.setShow( @@ -119,6 +122,7 @@ class _ConnectionTabPageState extends State { key: ValueKey(id), id: id, sessionId: sessionId == null ? null : SessionID(sessionId), + tabWindowId: tabWindowId, password: args['password'], toolbarState: _toolbarState, tabController: tabController, @@ -148,43 +152,24 @@ class _ConnectionTabPageState extends State { .map((e) => '${e.key},${(e.page as RemotePage).ffi.sessionId}') .toList() .join(';'); - } else if (call.method == kWindowEventCloseForSeparateWindow) { - debugPrint('REMOVE ME ============================= ${call.arguments}'); - final peerId = call.arguments['peerId']; - final newWindowId = call.arguments['newWindowId']; - late RemotePage page; + } else if (call.method == kWindowEventGetCachedSessionData) { + // Ready to show new window and close old tab. + final peerId = call.arguments; try { - page = tabController.state.value.tabs.firstWhere((tab) { - return tab.key == peerId; - }).page as RemotePage; + final remotePage = tabController.state.value.tabs + .firstWhere((tab) => tab.key == peerId) + .page as RemotePage; + returnValue = remotePage.ffi.ffiModel.cachedPeerData.toString(); } catch (e) { - debugPrint('Failed to find tab for peerId $peerId'); - return false; + debugPrint('Failed to get cached session data: $e'); } - final sendRes = await rustDeskWinManager.call( - newWindowId, - kWindowEventSendNewWindowData, - page.ffi.ffiModel.cachedPeerData) as bool; - if (!sendRes) { - return false; + if (returnValue != null) { + closeSessionOnDispose[peerId] = false; + tabController.closeBy(peerId); } - // Pass the required data to new window. - closeSessionOnDispose[peerId] = false; - tabController.closeBy(peerId); - return true; - } else if (call.method == kWindowEventSendNewWindowData) { - if (peerId == null) { - return false; - } - if (tabController.state.value.tabs.isEmpty) { - return false; - } - final page = tabController.state.value.tabs[0].page as RemotePage; - page.ffi.ffiModel - .handleCachedPeerData(call.arguments as CachedPeerData, peerId!); - return true; } _update_remote_count(); + return returnValue; }); Future.delayed(Duration.zero, () { restoreWindowPosition( diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 6a72fc3a..9d6dec49 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -771,7 +771,7 @@ class ScreenAdjustor { updateScreen() async { final v = await rustDeskWinManager.call( WindowType.Main, kWindowGetWindowInfo, ''); - final String valueStr = v; + final String valueStr = v.result; if (valueStr.isEmpty) { _screen = null; } else { diff --git a/flutter/lib/models/desktop_render_texture.dart b/flutter/lib/models/desktop_render_texture.dart index f5937362..f8456e33 100644 --- a/flutter/lib/models/desktop_render_texture.dart +++ b/flutter/lib/models/desktop_render_texture.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:texture_rgba_renderer/texture_rgba_renderer.dart'; @@ -21,7 +20,6 @@ class RenderTexture { _sessionId = sessionId; textureRenderer.createTexture(_textureKey).then((id) async { - debugPrint("id: $id, texture_key: $_textureKey"); if (id != -1) { final ptr = await textureRenderer.getTexturePtr(_textureKey); platformFFI.registerTexture(sessionId, ptr); @@ -31,9 +29,11 @@ class RenderTexture { } } - destroy() async { + destroy(bool unregisterTexture) async { if (useTextureRender && _textureKey != -1 && _sessionId != null) { - platformFFI.registerTexture(_sessionId!, 0); + if (unregisterTexture) { + platformFFI.registerTexture(_sessionId!, 0); + } await textureRenderer.closeTexture(_textureKey); _textureKey = -1; } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 286e89a3..4d0e30e3 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'dart:math'; import 'dart:ui' as ui; +import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hbb/consts.dart'; @@ -50,6 +51,37 @@ class CachedPeerData { bool direct = false; CachedPeerData(); + + @override + String toString() { + return jsonEncode({ + 'updatePrivacyMode': updatePrivacyMode, + 'peerInfo': peerInfo, + 'cursorDataList': cursorDataList, + 'lastCursorId': lastCursorId, + 'secure': secure, + 'direct': direct, + }); + } + + static CachedPeerData? fromString(String s) { + try { + final map = jsonDecode(s); + final data = CachedPeerData(); + data.updatePrivacyMode = map['updatePrivacyMode']; + data.peerInfo = map['peerInfo']; + for (final cursorData in map['cursorDataList']) { + data.cursorDataList.add(cursorData); + } + data.lastCursorId = map['lastCursorId']; + data.secure = map['secure']; + data.direct = map['direct']; + return data; + } catch (e) { + debugPrint('Failed to parse CachedPeerData: $e'); + return null; + } + } } class FfiModel with ChangeNotifier { @@ -1628,7 +1660,6 @@ class FFI { /// dialogManager use late to ensure init after main page binding [globalKey] late final dialogManager = OverlayDialogManager(); - late final bool isSessionAdded; late final SessionID sessionId; late final ImageModel imageModel; // session late final FfiModel ffiModel; // session @@ -1647,7 +1678,6 @@ class FFI { late final ElevationModel elevationModel; // session FFI(SessionID? sId) { - isSessionAdded = sId != null; sessionId = sId ?? (isDesktop ? Uuid().v4obj() : _constSessionId); imageModel = ImageModel(WeakReference(this)); ffiModel = FfiModel(WeakReference(this)); @@ -1673,7 +1703,8 @@ class FFI { bool isRdp = false, String? switchUuid, String? password, - bool? forceRelay}) { + bool? forceRelay, + int? tabWindowId}) { closed = false; auditNote = ''; assert(!(isFileTransfer && isPortForward), 'more than one connect type'); @@ -1688,7 +1719,9 @@ class FFI { imageModel.id = id; cursorModel.id = id; } - if (!isSessionAdded) { + // If tabWindowId != null, this session is a "tab -> window" one. + // Else this session is a new one. + if (tabWindowId == null) { // ignore: unused_local_variable final addRes = bind.sessionAddSync( sessionId: sessionId, @@ -1709,9 +1742,25 @@ class FFI { // Preserved for the rgba data. stream.listen((message) { if (closed) return; - if (isSessionAdded && !isToNewWindowNotified.value) { - // bind.sessionReadyToNewWindow(sessionId: sessionId); - bind.sessionRefresh(sessionId: sessionId); + if (tabWindowId != null && !isToNewWindowNotified.value) { + // Session is read to be moved to a new window. + // Get the cached data and handle the cached data. + Future.delayed(Duration.zero, () async { + final cachedData = await DesktopMultiWindow.invokeMethod( + tabWindowId, kWindowEventGetCachedSessionData, id); + if (cachedData == null) { + // unreachable + debugPrint('Unreachable, the cached data is empty.'); + return; + } + final data = CachedPeerData.fromString(cachedData); + if (data == null) { + debugPrint('Unreachable, the cached data cannot be decoded.'); + return; + } + ffiModel.handleCachedPeerData(data, id); + await bind.sessionRefresh(sessionId: sessionId); + }); isToNewWindowNotified.value = true; } () async { diff --git a/flutter/lib/utils/multi_window_manager.dart b/flutter/lib/utils/multi_window_manager.dart index 4230182d..a8be78c7 100644 --- a/flutter/lib/utils/multi_window_manager.dart +++ b/flutter/lib/utils/multi_window_manager.dart @@ -54,12 +54,10 @@ class RustDeskMultiWindowManager { var params = { 'type': WindowType.RemoteDesktop.index, 'id': peerId, + 'tab_window_id': windowId, 'session_id': sessionId, }; - // It's better to use the window id that returned by _newSession. - // Do not pass original window id to _newSession, - // as this function cann't promise the necessary data is passed to new window. - final multiWindowRes = await _newSession( + await _newSession( false, WindowType.RemoteDesktop, kWindowEventNewRemoteDesktop, @@ -67,12 +65,6 @@ class RustDeskMultiWindowManager { _remoteDesktopWindows, jsonEncode(params), ); - // kWindowEventCloseForSeparateWindow will not only close the tab, but also pass the required data to new window. - await DesktopMultiWindow.invokeMethod( - windowId, kWindowEventCloseForSeparateWindow, { - 'peerId': peerId, - 'newWindowId': multiWindowRes.windowId, - }); } Future newSessionWindow( diff --git a/src/client.rs b/src/client.rs index 84f338c6..79855046 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2366,7 +2366,7 @@ pub trait Interface: Send + Clone + 'static + Sized { fn send(&self, data: Data); fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str); fn handle_login_error(&mut self, err: &str) -> bool; - fn handle_peer_info(&mut self, pi: PeerInfo, is_cached_pi: bool); + fn handle_peer_info(&mut self, pi: PeerInfo); fn on_error(&self, err: &str) { self.msgbox("error", "Error", err, ""); } diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index 9e78b17a..aaf426e2 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -125,18 +125,7 @@ impl Remote { .await { Ok((mut peer, direct, pk)) => { - let is_secured = peer.is_secured(); - #[cfg(feature = "flutter")] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - { - self.handler - .cache_flutter - .write() - .unwrap() - .is_secured_direct - .replace((is_secured, direct)); - } - self.handler.set_connection_type(is_secured, direct); // flutter -> connection_ready + self.handler.set_connection_type(peer.is_secured(), direct); // flutter -> connection_ready self.handler.update_direct(Some(direct)); if conn_type == ConnType::DEFAULT_CONN { self.handler @@ -1021,12 +1010,7 @@ impl Remote { } } Some(login_response::Union::PeerInfo(pi)) => { - #[cfg(feature = "flutter")] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - { - self.handler.cache_flutter.write().unwrap().pi = pi.clone(); - } - self.handler.handle_peer_info(pi, false); + self.handler.handle_peer_info(pi); #[cfg(not(feature = "flutter"))] self.check_clipboard_file_context(); if !(self.handler.is_file_transfer() || self.handler.is_port_forward()) { @@ -1073,22 +1057,9 @@ impl Remote { _ => {} }, Some(message::Union::CursorData(cd)) => { - #[cfg(feature = "flutter")] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - { - let mut lock = self.handler.cache_flutter.write().unwrap(); - if !lock.cursor_data.contains_key(&cd.id) { - lock.cursor_data.insert(cd.id, cd.clone()); - } - } self.handler.set_cursor_data(cd); } Some(message::Union::CursorId(id)) => { - #[cfg(feature = "flutter")] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - { - self.handler.cache_flutter.write().unwrap().cursor_id = id; - } self.handler.set_cursor_id(id.to_string()); } Some(message::Union::CursorPosition(cp)) => { @@ -1305,16 +1276,6 @@ impl Remote { } } Some(misc::Union::SwitchDisplay(s)) => { - #[cfg(feature = "flutter")] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - { - self.handler - .cache_flutter - .write() - .unwrap() - .sp - .replace(s.clone()); - } self.handler.handle_peer_switch_display(&s); self.video_sender.send(MediaData::Reset).ok(); if s.width > 0 && s.height > 0 { @@ -1506,12 +1467,6 @@ impl Remote { } } Some(message::Union::PeerInfo(pi)) => { - #[cfg(feature = "flutter")] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - { - self.handler.cache_flutter.write().unwrap().pi.displays = - pi.displays.clone(); - } self.handler.set_displays(&pi.displays); } _ => {} diff --git a/src/flutter.rs b/src/flutter.rs index 52190ce2..1feb6b11 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -36,9 +36,11 @@ pub(crate) const APP_TYPE_CM: &str = "cm"; #[cfg(any(target_os = "android", target_os = "ios"))] pub(crate) const APP_TYPE_CM: &str = "main"; -pub(crate) const APP_TYPE_DESKTOP_REMOTE: &str = "remote"; -pub(crate) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; -pub(crate) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward"; +// Do not remove the following constants. +// Uncomment them when they are used. +// pub(crate) const APP_TYPE_DESKTOP_REMOTE: &str = "remote"; +// pub(crate) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; +// pub(crate) const APP_TYPE_DESKTOP_PORT_FORWARD: &str = "port forward"; lazy_static::lazy_static! { pub(crate) static ref CUR_SESSION_ID: RwLock = Default::default(); diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index ab7acc10..c36d5666 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -597,14 +597,6 @@ pub fn session_change_resolution(session_id: SessionID, display: i32, width: i32 } } -pub fn session_ready_to_new_window(session_id: SessionID) { - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if let Some(session) = SESSIONS.write().unwrap().get_mut(&session_id) { - session.restore_flutter_cache(); - session.refresh_video(); - } -} - pub fn session_set_size(_session_id: SessionID, _width: usize, _height: usize) { #[cfg(feature = "flutter_texture_render")] if let Some(session) = SESSIONS.write().unwrap().get_mut(&_session_id) { diff --git a/src/port_forward.rs b/src/port_forward.rs index 1e918cce..6a087abe 100644 --- a/src/port_forward.rs +++ b/src/port_forward.rs @@ -146,7 +146,7 @@ async fn connect_and_login( return Ok(None); } Some(login_response::Union::PeerInfo(pi)) => { - interface.handle_peer_info(pi, false); + interface.handle_peer_info(pi); break; } _ => {} diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 29828d6b..16fa5963 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -325,6 +325,7 @@ impl IpcTaskRunner { // for tmp use, without real conn id let mut write_jobs: Vec = Vec::new(); + #[cfg(windows)] let is_authorized = self.cm.is_authorized(self.conn_id); #[cfg(windows)] diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 1fdff814..eb902e3a 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -48,17 +48,6 @@ pub static IS_IN: AtomicBool = AtomicBool::new(false); const CHANGE_RESOLUTION_VALID_TIMEOUT_SECS: u64 = 15; -#[cfg(feature = "flutter")] -#[cfg(not(any(target_os = "android", target_os = "ios")))] -#[derive(Default)] -pub struct CacheFlutter { - pub pi: PeerInfo, - pub sp: Option, - pub cursor_data: HashMap, - pub cursor_id: u64, - pub is_secured_direct: Option<(bool, bool)>, -} - #[derive(Clone, Default)] pub struct Session { pub session_id: SessionID, // different from the one in LoginConfigHandler, used for flutter UI message pass @@ -73,9 +62,6 @@ pub struct Session { pub server_file_transfer_enabled: Arc>, pub server_clipboard_enabled: Arc>, pub last_change_display: Arc>, - #[cfg(feature = "flutter")] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - pub cache_flutter: Arc>, } #[derive(Clone)] @@ -1095,7 +1081,7 @@ impl Interface for Session { handle_login_error(self.lc.clone(), err, self) } - fn handle_peer_info(&mut self, mut pi: PeerInfo, is_cached_pi: bool) { + fn handle_peer_info(&mut self, mut pi: PeerInfo) { log::debug!("handle_peer_info :{:?}", pi); pi.username = self.lc.read().unwrap().get_username(&pi); if pi.current_display as usize >= pi.displays.len() { @@ -1116,12 +1102,10 @@ impl Interface for Session { self.msgbox("error", "Remote Error", "No Display", ""); return; } - if !is_cached_pi { - self.try_change_init_resolution(pi.current_display); - let p = self.lc.read().unwrap().should_auto_login(); - if !p.is_empty() { - input_os_password(p, true, self.clone()); - } + self.try_change_init_resolution(pi.current_display); + let p = self.lc.read().unwrap().should_auto_login(); + if !p.is_empty() { + input_os_password(p, true, self.clone()); } let current = &pi.displays[pi.current_display as usize]; self.set_display( @@ -1222,23 +1206,6 @@ impl Session { pub fn ctrl_alt_del(&self) { self.send_key_event(&crate::keyboard::client::event_ctrl_alt_del()); } - - #[cfg(feature = "flutter")] - #[cfg(not(any(target_os = "android", target_os = "ios")))] - pub fn restore_flutter_cache(&mut self) { - if let Some((is_secured, direct)) = self.cache_flutter.read().unwrap().is_secured_direct { - self.set_connection_type(is_secured, direct); - } - let pi = self.cache_flutter.read().unwrap().pi.clone(); - self.handle_peer_info(pi, true); - if let Some(sp) = self.cache_flutter.read().unwrap().sp.as_ref() { - self.handle_peer_switch_display(sp); - } - for (_, cd) in self.cache_flutter.read().unwrap().cursor_data.iter() { - self.set_cursor_data(cd.clone()); - } - self.set_cursor_id(self.cache_flutter.read().unwrap().cursor_id.to_string()); - } } #[tokio::main(flavor = "current_thread")] From 9adac5686be4914134e124162694b38338c66c2a Mon Sep 17 00:00:00 2001 From: dignow Date: Wed, 16 Aug 2023 21:52:03 +0800 Subject: [PATCH 19/37] fix unregister texture condition Signed-off-by: dignow --- flutter/lib/desktop/pages/remote_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index 2daffacc..0d51cabd 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -207,7 +207,7 @@ class _RemotePageState extends State // https://github.com/flutter/flutter/issues/64935 super.dispose(); debugPrint("REMOTE PAGE dispose session $sessionId ${widget.id}"); - await _renderTexture.destroy(widget.tabWindowId != null); + await _renderTexture.destroy(closeSession); // ensure we leave this session, this is a double check bind.sessionEnterOrLeave(sessionId: sessionId, enter: false); DesktopMultiWindow.removeListener(this); From afe12154a240823c5085c97058417f2de587a859 Mon Sep 17 00:00:00 2001 From: jimmyGALLAND <64364019+jimmyGALLAND@users.noreply.github.com> Date: Mon, 21 Aug 2023 16:26:23 +0200 Subject: [PATCH 20/37] Improve some translate fr.rs --- src/lang/fr.rs | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 16e0585e..f9d711cc 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -75,7 +75,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Do you want to enter again?", "Voulez-vous participer à nouveau ?"), ("Connection Error", "Erreur de connexion"), ("Error", "Erreur"), - ("Reset by the peer", "La connexion a été fermée par la machine distante"), + ("Reset by the peer", "La connexion a été fermée par l'appareil distant"), ("Connecting...", "Connexion..."), ("Connection in progress. Please wait.", "Connexion en cours. Veuillez patienter."), ("Please try 1 minute later", "Réessayez dans une minute"), @@ -92,8 +92,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Refresh File", "Rafraîchir le contenu"), ("Local", "Local"), ("Remote", "Distant"), - ("Remote Computer", "Ordinateur distant"), - ("Local Computer", "Ordinateur local"), + ("Remote Computer", "Appareil distant"), + ("Local Computer", "Appareil local"), ("Confirm Delete", "Confirmer la suppression"), ("Delete", "Supprimer"), ("Properties", "Propriétés"), @@ -129,9 +129,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Show remote cursor", "Afficher le curseur distant"), ("Show quality monitor", "Afficher le moniteur de qualité"), ("Disable clipboard", "Désactiver le presse-papier"), - ("Lock after session end", "Verrouiller l'ordinateur distant après la déconnexion"), + ("Lock after session end", "Verrouiller l'appareil distant après la déconnexion"), ("Insert", "Envoyer"), - ("Insert Lock", "Verrouiller l'ordinateur distant"), + ("Insert Lock", "Verrouiller l'appareil distant"), ("Refresh", "Rafraîchir l'écran"), ("ID does not exist", "L'ID n'existe pas"), ("Failed to connect to rendezvous server", "Échec de la connexion au serveur rendezvous"), @@ -188,7 +188,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Relayed and encrypted connection", "Connexion relais chiffrée"), ("Direct and unencrypted connection", "Connexion directe non chiffrée"), ("Relayed and unencrypted connection", "Connexion relais non chiffrée"), - ("Enter Remote ID", "Entrer l'ID de l'appareil à distance"), + ("Enter Remote ID", "Entrer l'ID de l'appareil distant"), ("Enter your password", "Entrer votre mot de passe"), ("Logging in...", "En cours de connexion ..."), ("Enable RDP session sharing", "Activer le partage de session RDP"), @@ -210,7 +210,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Settings", "Paramètres"), ("Username", " Nom d'utilisateur"), ("Invalid port", "Port invalide"), - ("Closed manually by the peer", "Fermé manuellement par la machine distante"), + ("Closed manually by the peer", "Fermé manuellement par l'appareil distant"), ("Enable remote configuration modification", "Autoriser la modification de la configuration à distance"), ("Run without install", "Exécuter sans installer"), ("Connect via relay", "Connexion via relais"), @@ -223,12 +223,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Verification code", "Code de vérification"), ("verification_tip", "Un nouvel appareil a été détecté et un code de vérification a été envoyé à l'adresse e-mail enregistrée, entrez le code de vérification pour continuer la connexion."), ("Logout", "Déconnexion"), - ("Tags", "Étiqueter"), + ("Tags", "Étiquettes"), ("Search ID", "Rechercher un ID"), ("whitelist_sep", "Vous pouvez utiliser une virgule, un point-virgule, un espace ou une nouvelle ligne comme séparateur"), ("Add ID", "Ajouter un ID"), ("Add Tag", "Ajouter une balise"), - ("Unselect all tags", "Désélectionner toutes les balises"), + ("Unselect all tags", "Désélectionner toutes les étiquettes"), ("Network error", "Erreur réseau"), ("Username missed", "Nom d'utilisateur manquant"), ("Password missed", "Mot de passe manquant"), @@ -301,9 +301,9 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Succeeded", "Succès"), ("Someone turns on privacy mode, exit", "Quelqu'un active le mode de confidentialité, quittez"), ("Unsupported", "Non pris en charge"), - ("Peer denied", "Machine distante refusée"), + ("Peer denied", "Appareil distant refusé"), ("Please install plugins", "Veuillez installer les plugins"), - ("Peer exit", "Machine distante déconnectée"), + ("Peer exit", "Appareil distant déconnecté"), ("Failed to turn off", "Échec de la désactivation"), ("Turned off", "Désactivé"), ("In privacy mode", "en mode privé"), @@ -382,7 +382,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Write a message", "Ecrire un message"), ("Prompt", ""), ("Please wait for confirmation of UAC...", "Veuillez attendre la confirmation de l'UAC..."), - ("elevated_foreground_window_tip", "La fenêtre actuelle que la machine distante nécessite des privilèges plus élevés pour fonctionner, elle ne peut donc pas être atteinte par la souris et le clavier. Vous pouvez demander à l'utilisateur distant de réduire la fenêtre actuelle ou de cliquer sur le bouton d'élévation dans la fenêtre de gestion des connexions. Pour éviter ce problème, il est recommandé d'installer le logiciel sur l'appareil distant."), + ("elevated_foreground_window_tip", "La fenêtre actuelle que l'appareil distant nécessite des privilèges plus élevés pour fonctionner, elle ne peut donc pas être atteinte par la souris et le clavier. Vous pouvez demander à l'utilisateur distant de réduire la fenêtre actuelle ou de cliquer sur le bouton d'élévation dans la fenêtre de gestion des connexions. Pour éviter ce problème, il est recommandé d'installer le logiciel sur l'appareil distant."), ("Disconnected", "Déconnecté"), ("Other", "Divers"), ("Confirm before closing multiple tabs", "Confirmer avant de fermer plusieurs onglets"), @@ -392,7 +392,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Wayland requires Ubuntu 21.04 or higher version.", "Wayland nécessite Ubuntu 21.04 ou une version supérieure."), ("Wayland requires higher version of linux distro. Please try X11 desktop or change your OS.", "Wayland nécessite une version supérieure de la distribution Linux. Veuillez essayer le bureau X11 ou changer votre système d'exploitation."), ("JumpLink", "Afficher"), - ("Please Select the screen to be shared(Operate on the peer side).", "Veuillez sélectionner l'écran à partager (côté machine distante)."), + ("Please Select the screen to be shared(Operate on the peer side).", "Veuillez sélectionner l'écran à partager (côté appareil distant)."), ("Show RustDesk", "Afficher RustDesk"), ("This PC", "Ce PC"), ("or", "ou"), @@ -454,7 +454,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Voice call", "Appel voix"), ("Text chat", "Conversation textuelfle"), ("Stop voice call", "Stopper l'appel voix"), - ("relay_hint_tip", "Il se peut qu'il ne doit pas possible de se connecter directement, vous pouvez essayer de vous connecter via un relais. \nEn outre, si vous souhaitez utiliser directement le relais, vous pouvez ajouter le suffixe \"/r\" à l'ID ou sélectionner l'option \"Toujours se connecter via le relais\" dans la fiche pair."), + ("relay_hint_tip", "Il se peut qu'il ne doit pas possible de se connecter directement, vous pouvez essayer de vous connecter via un relais. \nEn outre, si vous souhaitez utiliser directement le relais, vous pouvez ajouter le suffixe \"/r\" à l'ID ou sélectionner l'option \"Toujours se connecter via le relais\" dans la fiche appareils distants."), ("Reconnect", "Se reconnecter"), ("Codec", "Codec"), ("Resolution", "Résolution"), @@ -470,14 +470,14 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Maximize", "Maximiser"), ("Your Device", "Votre appareil"), ("empty_recent_tip", "Oups, pas de sessions récentes!\nIl est temps d'en prévoir une nouvelle."), - ("empty_favorite_tip", "Vous n'avez pas encore de pairs favoris?\nTrouvons quelqu'un avec qui vous connecter et ajoutez-le à vos favoris!"), - ("empty_lan_tip", "Oh non, il semble que nous n'ayons pas encore de pairs découverts."), - ("empty_address_book_tip", "Ouh là là! il semble qu'il n'y ait actuellement aucun pair répertorié dans votre carnet d'adresses."), + ("empty_favorite_tip", "Vous n'avez pas encore d'appareils distants favorits?\nTrouvons quelqu'un avec qui vous connecter et ajoutez-la à vos favoris!"), + ("empty_lan_tip", "Oh non, il semble que nous n'ayons pas encore d'appareil réseaux local découverts."), + ("empty_address_book_tip", "Ouh là là! il semble qu'il n'y ait actuellement aucun appareil distant répertorié dans votre carnet d'adresses."), ("eg: admin", "ex: admin"), ("Empty Username", "Nom d'utilisation non spécifié"), ("Empty Password", "Mot de passe non spécifié"), ("Me", "Moi"), - ("identical_file_tip", "Ce fichier est identique à celui du pair."), + ("identical_file_tip", "Ce fichier est identique à celui de l'appareil distant."), ("show_monitors_tip", "Afficher les moniteurs dans la barre d'outils"), ("View Mode", "Mode vue"), ("login_linux_tip", "Se connecter au compte Linux distant"), @@ -498,8 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Fingerprint", "Empreinte digitale"), ("Copy Fingerprint", "Copier empreinte digitale"), ("no fingerprints", "Pas d'empreintes digitales"), - ("Select a peer", "Sélectionnez la machine distante"), - ("Select peers", "Sélectionnez des machines distantes"), + ("Select a peer", "Sélectionnez l'appareil distant"), + ("Select peers", "Sélectionnez des appareils distants"), ("Plugins", "Plugins"), ("Uninstall", "Désinstaller"), ("Update", "Mise à jour"), @@ -523,7 +523,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Stop", "Stopper"), ("exceed_max_devices", "Vous avez atteint le nombre maximal d'appareils gérés."), ("Sync with recent sessions", "Synchroniser avec les sessions récentes"), - ("Sort tags", "Trier les Tags"), + ("Sort tags", "Trier les étiquettes"), ("Open connection in new tab", "Ouvrir la connexion dans un nouvel onglet"), ("Move tab to new window", "Déplacer l'onglet vers une nouvelle fenêtre"), ("Can not be empty", "Ne peux pas être vide"), @@ -534,7 +534,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Grid View", "Vue Grille"), ("List View", "Vue Liste"), ("Select", "Sélectionner"), - ("Toggle Tags", "Basculer vers les Tags"), + ("Toggle Tags", "Basculer vers les étiquettes"), ("pull_ab_failed_tip", "Impossible d'actualiser le carnet d'adresses"), ("push_ab_failed_tip", "Échec de la synchronisation du carnet d'adresses"), ("synced_peer_readded_tip", "Les appareils qui étaient présents dans les sessions récentes seront synchronisés avec le carnet d'adresses."), From d8ace7e38bd598aba3c1bc8ae15e423d190e660c Mon Sep 17 00:00:00 2001 From: Jimmy GALLAND Date: Mon, 21 Aug 2023 22:42:16 +0200 Subject: [PATCH 21/37] fix peer tab page count selected translate --- flutter/lib/common/widgets/peer_tab_page.dart | 2 +- src/lang/fr.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index 120ea7ff..3f416147 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -426,7 +426,7 @@ class _PeerTabPageState extends State Widget selectionCount(int count) { return Align( alignment: Alignment.center, - child: Text('$count selected'), + child: Text('$count ${translate('Selected')}'), ); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index f9d711cc..fdf9cdf5 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -274,7 +274,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Chat", "Discuter"), ("Total", "Total"), ("items", "éléments"), - ("Selected", "Sélectionné"), + ("Selected", "Sélectionné(s)"), ("Screen Capture", "Capture d'écran"), ("Input Control", "Contrôle de saisie"), ("Audio Capture", "Capture audio"), From 83d9cb55f1ae5e622ae689ac8f136d283e870e5b Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 22 Aug 2023 08:54:01 +0800 Subject: [PATCH 22/37] filter tags with union, not intersection Signed-off-by: 21pages --- flutter/lib/common/widgets/peers_view.dart | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index b4fd8e1d..0e4898fc 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -421,15 +421,12 @@ class AddressBookPeersView extends BasePeersView { if (selectedTags.isEmpty) { return true; } - if (idents.isEmpty) { - return false; - } for (final tag in selectedTags) { - if (!idents.contains(tag)) { - return false; + if (idents.contains(tag)) { + return true; } } - return true; + return false; } } From a48532d0b1c36f5fa1b5933e8bee3f52b50b9ebd Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 22 Aug 2023 09:01:11 +0800 Subject: [PATCH 23/37] fix mobile missing tag color Signed-off-by: 21pages --- flutter/lib/common/widgets/peer_card.dart | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 889ba3fb..fd558c68 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -66,7 +66,7 @@ class _PeerCardState extends State<_PeerCard> final name = '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; final PeerTabModel peerTabModel = Provider.of(context); - return Card( + final child = Card( margin: EdgeInsets.symmetric(horizontal: 2), child: GestureDetector( onTap: () { @@ -115,6 +115,23 @@ class _PeerCardState extends State<_PeerCard> ], ), ))); + final colors = _frontN(peer.tags, 25).map((e) => str2color2(e)).toList(); + return Tooltip( + message: peer.tags.isNotEmpty + ? '${translate('Tags')}: ${peer.tags.join(', ')}' + : '', + child: Stack(children: [ + child, + if (colors.isNotEmpty) + Positioned( + top: 2, + right: 10, + child: CustomPaint( + painter: TagPainter(radius: 3, colors: colors), + ), + ) + ]), + ); } Widget _buildDesktop() { From 5a6a7e8d82c5e0022ce72731856331fa7769511e Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 22 Aug 2023 11:41:57 +0800 Subject: [PATCH 24/37] mobile use _buildPeerTile Signed-off-by: 21pages --- flutter/lib/common/widgets/peer_card.dart | 224 +++++++++------------- 1 file changed, 93 insertions(+), 131 deletions(-) diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index fd558c68..8bb4fdfd 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -63,75 +63,29 @@ class _PeerCardState extends State<_PeerCard> Widget _buildMobile() { final peer = super.widget.peer; - final name = - '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; final PeerTabModel peerTabModel = Provider.of(context); - final child = Card( + return Card( margin: EdgeInsets.symmetric(horizontal: 2), child: GestureDetector( - onTap: () { - if (peerTabModel.multiSelectionMode) { - peerTabModel.select(peer); - } else { - if (!isWebDesktop) { - connectInPeerTab(context, peer.id, widget.tab); - } - } - }, - onDoubleTap: isWebDesktop - ? () => connectInPeerTab(context, peer.id, widget.tab) - : null, - onLongPress: () { + onTap: () { + if (peerTabModel.multiSelectionMode) { peerTabModel.select(peer); - }, - child: Container( + } else { + if (!isWebDesktop) { + connectInPeerTab(context, peer.id, widget.tab); + } + } + }, + onDoubleTap: isWebDesktop + ? () => connectInPeerTab(context, peer.id, widget.tab) + : null, + onLongPress: () { + peerTabModel.select(peer); + }, + child: Container( padding: EdgeInsets.only(left: 12, top: 8, bottom: 8), - child: Row( - children: [ - Container( - width: 50, - height: 50, - decoration: BoxDecoration( - color: str2color('${peer.id}${peer.platform}', 0x7f), - borderRadius: BorderRadius.circular(4), - ), - padding: const EdgeInsets.all(6), - child: getPlatformImage(peer.platform)), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row(children: [ - getOnline(4, peer.online), - Text(peer.alias.isEmpty - ? formatID(peer.id) - : peer.alias) - ]), - Text(name) - ], - ).paddingOnly(left: 8.0), - ), - checkBoxOrActionMoreMobile(peer), - ], - ), - ))); - final colors = _frontN(peer.tags, 25).map((e) => str2color2(e)).toList(); - return Tooltip( - message: peer.tags.isNotEmpty - ? '${translate('Tags')}: ${peer.tags.join(', ')}' - : '', - child: Stack(children: [ - child, - if (colors.isNotEmpty) - Positioned( - top: 2, - right: 10, - child: CustomPaint( - painter: TagPainter(radius: 3, colors: colors), - ), - ) - ]), - ); + child: _buildPeerTile(context, peer, null)), + )); } Widget _buildDesktop() { @@ -178,87 +132,95 @@ class _PeerCardState extends State<_PeerCard> } Widget _buildPeerTile( - BuildContext context, Peer peer, Rx deco) { + BuildContext context, Peer peer, Rx? deco) { final name = '${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}'; final greyStyle = TextStyle( fontSize: 11, color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6)); - final child = Obx( - () => Container( - foregroundDecoration: deco.value, - child: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Container( - decoration: BoxDecoration( - color: str2color('${peer.id}${peer.platform}', 0x7f), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(_tileRadius), - bottomLeft: Radius.circular(_tileRadius), - ), - ), - alignment: Alignment.center, - width: 42, - child: getPlatformImage(peer.platform, size: 30).paddingAll(6), - ), - Expanded( - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: BorderRadius.only( - topRight: Radius.circular(_tileRadius), - bottomRight: Radius.circular(_tileRadius), + final child = Row( + mainAxisSize: MainAxisSize.max, + children: [ + Container( + decoration: BoxDecoration( + color: str2color('${peer.id}${peer.platform}', 0x7f), + borderRadius: isMobile + ? BorderRadius.circular(_tileRadius) + : BorderRadius.only( + topLeft: Radius.circular(_tileRadius), + bottomLeft: Radius.circular(_tileRadius), ), - ), - child: Row( - children: [ - Expanded( - child: Column( - children: [ - Row(children: [ - getOnline(8, peer.online), - Expanded( - child: Text( - peer.alias.isEmpty - ? formatID(peer.id) - : peer.alias, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.titleSmall, - )), - ]).marginOnly(bottom: 0, top: 2), - Align( - alignment: Alignment.centerLeft, - child: Text( - name, - style: greyStyle, - textAlign: TextAlign.start, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ).marginOnly(top: 2), - ), - checkBoxOrActionMoreDesktop(peer, isTile: true), - ], - ).paddingOnly(left: 10.0, top: 3.0), - ), - ) - ], + ), + alignment: Alignment.center, + width: isMobile ? 50 : 42, + height: isMobile ? 50 : null, + child: getPlatformImage(peer.platform, size: isMobile ? 38 : 30) + .paddingAll(6), ), - ), + Expanded( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: BorderRadius.only( + topRight: Radius.circular(_tileRadius), + bottomRight: Radius.circular(_tileRadius), + ), + ), + child: Row( + children: [ + Expanded( + child: Column( + children: [ + Row(children: [ + getOnline(isMobile ? 4 : 8, peer.online), + Expanded( + child: Text( + peer.alias.isEmpty ? formatID(peer.id) : peer.alias, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.titleSmall, + )), + ]).marginOnly(top: isMobile ? 0 : 2), + Align( + alignment: Alignment.centerLeft, + child: Text( + name, + style: isMobile ? null : greyStyle, + textAlign: TextAlign.start, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ).marginOnly(top: 2), + ), + isMobile + ? checkBoxOrActionMoreMobile(peer) + : checkBoxOrActionMoreDesktop(peer, isTile: true), + ], + ).paddingOnly(left: 10.0, top: 3.0), + ), + ) + ], ); final colors = _frontN(peer.tags, 25).map((e) => str2color2(e)).toList(); return Tooltip( - message: peer.tags.isNotEmpty - ? '${translate('Tags')}: ${peer.tags.join(', ')}' - : '', + message: isMobile + ? '' + : peer.tags.isNotEmpty + ? '${translate('Tags')}: ${peer.tags.join(', ')}' + : '', child: Stack(children: [ - child, + deco == null + ? child + : Obx( + () => Container( + foregroundDecoration: deco.value, + child: child, + ), + ), if (colors.isNotEmpty) Positioned( top: 2, - right: 10, + right: isMobile ? 20 : 10, child: CustomPaint( painter: TagPainter(radius: 3, colors: colors), ), From b27c3ff169b56fd6d44c87179897f10c695c4c0b Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 22 Aug 2023 19:07:01 +0800 Subject: [PATCH 25/37] change tag color Signed-off-by: 21pages --- flutter/lib/common.dart | 16 ++- flutter/lib/common/widgets/address_book.dart | 22 +++- flutter/lib/common/widgets/peer_card.dart | 6 +- flutter/lib/models/ab_model.dart | 117 ++++++++++++------- flutter/pubspec.yaml | 1 + libs/hbb_common/src/config.rs | 6 + src/lang/ca.rs | 1 + src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/el.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/fa.rs | 1 + src/lang/fr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ja.rs | 1 + src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/lt.rs | 1 + src/lang/nl.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_PT.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ro.rs | 1 + src/lang/ru.rs | 1 + src/lang/sk.rs | 1 + src/lang/sl.rs | 1 + src/lang/sq.rs | 1 + src/lang/sr.rs | 1 + src/lang/sv.rs | 1 + src/lang/template.rs | 1 + src/lang/th.rs | 1 + src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + src/lang/ua.rs | 1 + src/lang/vn.rs | 1 + 40 files changed, 152 insertions(+), 50 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index eb21ac82..48323d5f 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -1077,7 +1077,7 @@ Color str2color(String str, [alpha = 0xFF]) { return Color((hash & 0xFF7FFF) | (alpha << 24)); } -Color str2color2(String str, [alpha = 0xFF]) { +Color str2color2(String str, {List existing = const []}) { Map colorMap = { "red": Colors.red, "green": Colors.green, @@ -1094,10 +1094,10 @@ Color str2color2(String str, [alpha = 0xFF]) { }; final color = colorMap[str.toLowerCase()]; if (color != null) { - return color.withAlpha(alpha); + return color.withAlpha(0xFF); } if (str.toLowerCase() == 'yellow') { - return Colors.yellow.withAlpha(alpha); + return Colors.yellow.withAlpha(0xFF); } var hash = 0; for (var i = 0; i < str.length; i++) { @@ -1105,7 +1105,15 @@ Color str2color2(String str, [alpha = 0xFF]) { } List colorList = colorMap.values.toList(); hash = hash % colorList.length; - return colorList[hash].withAlpha(alpha); + var result = colorList[hash].withAlpha(0xFF); + if (existing.contains(result.value)) { + Color? notUsed = + colorList.firstWhereOrNull((e) => !existing.contains(e.value)); + if (notUsed != null) { + result = notUsed; + } + } + return result; } const K = 1024; diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index 86a5b2c5..292266ae 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -7,6 +7,7 @@ import 'package:flutter_hbb/models/ab_model.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu; import 'package:get/get.dart'; +import 'package:flex_color_picker/flex_color_picker.dart'; import '../../common.dart'; import 'dialog.dart'; @@ -513,7 +514,7 @@ class AddressBookTag extends StatelessWidget { child: Obx(() => Container( decoration: BoxDecoration( color: tags.contains(name) - ? str2color2(name, 0xFF) + ? gFFI.abModel.getTagColor(name) : Theme.of(context).colorScheme.background, borderRadius: BorderRadius.circular(4)), margin: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), @@ -528,7 +529,7 @@ class AddressBookTag extends StatelessWidget { shape: BoxShape.circle, color: tags.contains(name) ? Colors.white - : str2color2(name)), + : gFFI.abModel.getTagColor(name)), ).marginOnly(right: radius / 2), Expanded( child: Text(name, @@ -568,6 +569,23 @@ class AddressBookTag extends StatelessWidget { Future.delayed(Duration.zero, () => Get.back()); }); }), + getEntry(translate(translate('Change Color')), () async { + final model = gFFI.abModel; + Color oldColor = model.getTagColor(name); + Color newColor = await showColorPickerDialog( + context, + oldColor, + pickersEnabled: { + ColorPickerType.accent: false, + ColorPickerType.wheel: true, + }, + showColorCode: true, + ); + if (oldColor != newColor) { + model.setTagColor(name, newColor); + model.pushAb(); + } + }), getEntry(translate("Delete"), () { gFFI.abModel.deleteTag(name); gFFI.abModel.pushAb(); diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 8bb4fdfd..74f0dcb3 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -201,7 +201,8 @@ class _PeerCardState extends State<_PeerCard> ) ], ); - final colors = _frontN(peer.tags, 25).map((e) => str2color2(e)).toList(); + final colors = + _frontN(peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList(); return Tooltip( message: isMobile ? '' @@ -311,7 +312,8 @@ class _PeerCardState extends State<_PeerCard> ), ); - final colors = _frontN(peer.tags, 25).map((e) => str2color2(e)).toList(); + final colors = + _frontN(peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList(); return Tooltip( message: peer.tags.isNotEmpty ? '${translate('Tags')}: ${peer.tags.join(', ')}' diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index f5e21747..e91e42ef 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -28,6 +28,7 @@ class AbModel { final pullError = "".obs; final pushError = "".obs; final tags = [].obs; + final RxMap tagColors = Map.fromEntries([]).obs; final peers = List.empty(growable: true).obs; final sortTags = shouldSortTags().obs; final retrying = false.obs; @@ -80,10 +81,11 @@ class AbModel { if (resp.body.toLowerCase() == "null") { // normal reply, emtpy ab return null tags.clear(); + tagColors.clear(); peers.clear(); } else if (resp.body.isNotEmpty) { Map json = - _jsonDecode(utf8.decode(resp.bodyBytes), resp.statusCode); + _jsonDecodeResp(utf8.decode(resp.bodyBytes), resp.statusCode); if (json.containsKey('error')) { throw json['error']; } else if (json.containsKey('data')) { @@ -93,26 +95,7 @@ class AbModel { } catch (e) {} final data = jsonDecode(json['data']); if (data != null) { - final oldOnlineIDs = - peers.where((e) => e.online).map((e) => e.id).toList(); - tags.clear(); - peers.clear(); - if (data['tags'] is List) { - tags.value = data['tags']; - } - if (data['peers'] is List) { - for (final peer in data['peers']) { - peers.add(Peer.fromJson(peer)); - } - } - if (isFull(false)) { - peers.removeRange(licensedDevices, peers.length); - } - // restore online - peers - .where((e) => oldOnlineIDs.contains(e.id)) - .map((e) => e.online = true) - .toList(); + _deserialize(data); _saveCache(); // save on success } } @@ -242,10 +225,7 @@ class AbModel { final api = "${await bind.mainGetApiServer()}/api/ab"; var authHeaders = getHttpHeaders(); authHeaders['Content-Type'] = "application/json"; - final peersJsonData = peers.map((e) => e.toAbUploadJson()).toList(); - final body = jsonEncode({ - "data": jsonEncode({"tags": tags, "peers": peersJsonData}) - }); + final body = jsonEncode(_serialize()); http.Response resp; // support compression if (licensedDevices > 0 && body.length > 1024) { @@ -261,7 +241,7 @@ class AbModel { ret = true; _saveCache(); } else { - Map json = _jsonDecode(resp.body, resp.statusCode); + Map json = _jsonDecodeResp(resp.body, resp.statusCode); if (json.containsKey('error')) { throw json['error']; } else if (resp.statusCode == 200) { @@ -318,6 +298,7 @@ class AbModel { void deleteTag(String tag) { gFFI.abModel.selectedTags.remove(tag); tags.removeWhere((element) => element == tag); + tagColors.remove(tag); for (var peer in peers) { if (peer.tags.isEmpty) { continue; @@ -353,6 +334,11 @@ class AbModel { } }).toList(); } + int? oldColor = tagColors[oldTag]; + if (oldColor != null) { + tagColors.remove(oldTag); + tagColors.addAll({newTag: oldColor}); + } } void unsetSelectedTags() { @@ -368,6 +354,20 @@ class AbModel { } } + Color getTagColor(String tag) { + int? colorValue = tagColors[tag]; + if (colorValue != null) { + return Color(colorValue); + } + return str2color2(tag, existing: tagColors.values.toList()); + } + + setTagColor(String tag, Color color) { + if (tags.contains(tag)) { + tagColors[tag] = color.value; + } + } + void merge(Peer r, Peer p) { p.hash = r.hash.isEmpty ? p.hash : r.hash; p.username = r.username.isEmpty ? p.username : r.username; @@ -467,12 +467,10 @@ class AbModel { _saveCache() { try { - final peersJsonData = peers.map((e) => e.toAbUploadJson()).toList(); - final m = { + var m = _serialize(); + m.addAll({ "access_token": bind.mainGetLocalOption(key: 'access_token'), - "peers": peersJsonData, - "tags": tags.map((e) => e.toString()).toList(), - }; + }); bind.mainSaveAb(json: jsonEncode(m)); } catch (e) { debugPrint('ab save:$e'); @@ -488,22 +486,13 @@ class AbModel { final cache = await bind.mainLoadAb(); final data = jsonDecode(cache); if (data == null || data['access_token'] != access_token) return; - tags.clear(); - peers.clear(); - if (data['tags'] is List) { - tags.value = data['tags']; - } - if (data['peers'] is List) { - for (final peer in data['peers']) { - peers.add(Peer.fromJson(peer)); - } - } + _deserialize(data); } catch (e) { debugPrint("load ab cache: $e"); } } - Map _jsonDecode(String body, int statusCode) { + Map _jsonDecodeResp(String body, int statusCode) { try { Map json = jsonDecode(body); return json; @@ -516,6 +505,50 @@ class AbModel { } } + Map _serialize() { + final peersJsonData = peers.map((e) => e.toAbUploadJson()).toList(); + final tagColorJsonData = jsonEncode(tagColors); + return { + "tags": tags, + "peers": peersJsonData, + "tag_colors": tagColorJsonData + }; + } + + _deserialize(dynamic data) { + if (data == null) return; + final oldOnlineIDs = peers.where((e) => e.online).map((e) => e.id).toList(); + tags.clear(); + tagColors.clear(); + peers.clear(); + if (data['tags'] is List) { + tags.value = data['tags']; + } + if (data['peers'] is List) { + for (final peer in data['peers']) { + peers.add(Peer.fromJson(peer)); + } + } + if (isFull(false)) { + peers.removeRange(licensedDevices, peers.length); + } + // restore online + peers + .where((e) => oldOnlineIDs.contains(e.id)) + .map((e) => e.online = true) + .toList(); + if (data['tag_colors'] is String) { + Map map = jsonDecode(data['tag_colors']); + tagColors.value = Map.from(map); + } + // add color to tag + final tagsWithoutColor = + tags.toList().where((e) => !tagColors.containsKey(e)).toList(); + for (var t in tagsWithoutColor) { + tagColors[t] = str2color2(t, existing: tagColors.values.toList()).value; + } + } + reSyncToast(Future future) { if (!shouldSyncAb()) return; Future.delayed(Duration.zero, () async { diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index fc0be701..04af8f92 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -97,6 +97,7 @@ dependencies: dropdown_button2: ^2.0.0 uuid: ^3.0.7 auto_size_text_field: ^2.2.1 + flex_color_picker: ^3.3.0 dev_dependencies: icons_launcher: ^2.0.4 diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 2cb0072c..f40ff146 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -1525,6 +1525,12 @@ pub struct Ab { pub peers: Vec, #[serde(default, deserialize_with = "deserialize_vec_string")] pub tags: Vec, + #[serde( + default, + deserialize_with = "deserialize_string", + skip_serializing_if = "String::is_empty" + )] + pub tag_colors: String, } impl Ab { diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 739d5646..4801abca 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 34ecdb27..3313537d 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", "未成功获取地址簿"), ("push_ab_failed_tip", "未成功上传地址簿"), ("synced_peer_readded_tip", "最近会话中存在的设备将会被重新同步到地址簿。"), + ("Change Color", "更改颜色"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index e32246da..3d913b0d 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", "Nepodařilo se obnovit adresář"), ("push_ab_failed_tip", "Nepodařilo se synchronizovat adresář se serverem"), ("synced_peer_readded_tip", "Zařízení, která byla přítomna v posledních relacích, budou synchronizována zpět do adresáře."), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 830cb0c4..afaf9838 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 53a898a6..8eb066f3 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", "Aktualisierung des Adressbuchs fehlgeschlagen"), ("push_ab_failed_tip", "Synchronisierung des Adressbuchs mit dem Server fehlgeschlagen"), ("synced_peer_readded_tip", "Die Geräte, die in den letzten Sitzungen vorhanden waren, werden erneut zum Adressbuch hinzugefügt."), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 46f81667..56805e57 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 8d7f1c87..bcb46783 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index aabbbfd2..5384dfea 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", "No se ha podido refrescar el directorio"), ("push_ab_failed_tip", "No se ha podido sincronizar el directorio con el servidor"), ("synced_peer_readded_tip", "Los dispositivos presentes en sesiones recientes se sincronizarán con el directorio."), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index eeb36af3..fab82f66 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index fdf9cdf5..24cc268e 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", "Impossible d'actualiser le carnet d'adresses"), ("push_ab_failed_tip", "Échec de la synchronisation du carnet d'adresses"), ("synced_peer_readded_tip", "Les appareils qui étaient présents dans les sessions récentes seront synchronisés avec le carnet d'adresses."), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 2da6bd72..aaf888d2 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 45cb3f1c..a7b12f2e 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", "Gagal memuat ulang buku alamat"), ("push_ab_failed_tip", "Gagal menyinkronkan buku alamat ke server"), ("synced_peer_readded_tip", "Perangkat yang terdaftar dalam sesi-sesi terbaru akan di-sinkronkan kembali ke buku alamat."), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 4febb9e0..3b8bd841 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", "Impossibile aggiornare la rubrica"), ("push_ab_failed_tip", "Impossibile sincronizzare la rubrica con il server"), ("synced_peer_readded_tip", "I dispositivi presenti nelle sessioni recenti saranno sincronizzati di nuovo nella rubrica."), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 4c4d6e14..1a9e8da4 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 91e49be3..28c34f7e 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 807b74ba..2fb2b8b9 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 0cd258b9..6a8c7e9f 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index c37e95c8..3b08427d 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 923dadc2..67b9d188 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 3eb85022..416600ee 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 40360be5..98e469f7 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index 50ae3990..f4d44b3a 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 5830525d..37fdb206 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", "Невозможно обновить адресную книгу"), ("push_ab_failed_tip", "Невозможно синхронизировать адресную книгу с сервером"), ("synced_peer_readded_tip", "Устройства, присутствовавшие в последних сеансах, будут синхронизированы с адресной книгой."), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 4b7ced04..8421bd2b 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 6b356a23..f4c5ff38 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 5eb5c0fb..11e3e1cc 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index f642bc12..49499947 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 76ea47cd..4c0f73f5 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 06591d14..ee7fb0d4 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index 6e21420f..e6f12d85 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 15aaac5f..bb3ab5de 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 6d9d8089..8f28395d 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", "未成功獲取地址簿"), ("push_ab_failed_tip", "未成功上傳地址簿"), ("synced_peer_readded_tip", "最近會話中存在的設備將會被重新同步到地址簿。"), + ("Change Color", "更改顏色"), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index acdf8f10..69ca7c2d 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 8aeafd93..7a0cce35 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -538,5 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", ""), ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), + ("Change Color", ""), ].iter().cloned().collect(); } From 7f7d5d9f4c9cf8e3eb91116663fbf250c298acab Mon Sep 17 00:00:00 2001 From: dignow Date: Tue, 22 Aug 2023 21:52:23 +0800 Subject: [PATCH 26/37] oidc, add default gitlab icon Signed-off-by: dignow --- flutter/assets/auth-gitlab.svg | 1 + flutter/lib/common/widgets/login.dart | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 flutter/assets/auth-gitlab.svg diff --git a/flutter/assets/auth-gitlab.svg b/flutter/assets/auth-gitlab.svg new file mode 100644 index 00000000..c4ec9d2e --- /dev/null +++ b/flutter/assets/auth-gitlab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/flutter/lib/common/widgets/login.dart b/flutter/lib/common/widgets/login.dart index 7c17e9de..b26397b9 100644 --- a/flutter/lib/common/widgets/login.dart +++ b/flutter/lib/common/widgets/login.dart @@ -14,6 +14,7 @@ import './dialog.dart'; const kOpSvgList = [ 'github', + 'gitlab', 'google', 'apple', 'okta', @@ -72,6 +73,11 @@ class ButtonOP extends StatelessWidget { @override Widget build(BuildContext context) { + final opLabel = { + 'github': 'GitHub', + 'gitlab': 'GitLab' + }[op.toLowerCase()] ?? + toCapitalized(op); return Row(children: [ Container( height: height, @@ -97,8 +103,7 @@ class ButtonOP extends StatelessWidget { child: FittedBox( fit: BoxFit.scaleDown, child: Center( - child: Text( - '${translate("Continue with")} ${op.toLowerCase() == "github" ? "GitHub" : toCapitalized(op)}')), + child: Text('${translate("Continue with")} $opLabel')), ), ), ], From 6666dece5d58e93b83c25530d9b9d8070dc135d3 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Tue, 22 Aug 2023 22:02:42 +0800 Subject: [PATCH 27/37] svgo gitlab.svg --- flutter/assets/auth-gitlab.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/assets/auth-gitlab.svg b/flutter/assets/auth-gitlab.svg index c4ec9d2e..9402e132 100644 --- a/flutter/assets/auth-gitlab.svg +++ b/flutter/assets/auth-gitlab.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file From 9cb778618209b6f43a9cf94fea7500aa39648578 Mon Sep 17 00:00:00 2001 From: SergeyMy <131688106+SergeyMy@users.noreply.github.com> Date: Tue, 22 Aug 2023 19:26:59 +0500 Subject: [PATCH 28/37] Update ru.rs --- src/lang/ru.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 37fdb206..74192a87 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -538,6 +538,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", "Невозможно обновить адресную книгу"), ("push_ab_failed_tip", "Невозможно синхронизировать адресную книгу с сервером"), ("synced_peer_readded_tip", "Устройства, присутствовавшие в последних сеансах, будут синхронизированы с адресной книгой."), - ("Change Color", ""), + ("Change Color", "Изменить цвет"), ].iter().cloned().collect(); } From 8427b03a394ff5f9f372ee224b10d697052c6737 Mon Sep 17 00:00:00 2001 From: Sahil Yeole Date: Wed, 23 Aug 2023 00:13:23 +0530 Subject: [PATCH 29/37] Using github latest release url to check for software update Signed-off-by: Sahil Yeole --- src/common.rs | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/common.rs b/src/common.rs index 011ca052..5ad92d91 100644 --- a/src/common.rs +++ b/src/common.rs @@ -831,30 +831,19 @@ pub fn check_software_update() { #[tokio::main(flavor = "current_thread")] async fn check_software_update_() -> hbb_common::ResultType<()> { - sleep(3.).await; + let url = "https://github.com/rustdesk/rustdesk/releases/latest"; + let latest_release_response = reqwest::get(url).await?; + let latest_release_version = latest_release_response + .url() + .path() + .rsplit('/') + .next() + .unwrap(); - let rendezvous_server = format!("rs-sg.rustdesk.com:{}", config::RENDEZVOUS_PORT); - let (mut socket, rendezvous_server) = - socket_client::new_udp_for(&rendezvous_server, CONNECT_TIMEOUT).await?; + let response_url = latest_release_response.url().to_string(); - let mut msg_out = RendezvousMessage::new(); - msg_out.set_software_update(SoftwareUpdate { - url: crate::VERSION.to_owned(), - ..Default::default() - }); - socket.send(&msg_out, rendezvous_server).await?; - use hbb_common::protobuf::Message; - for _ in 0..2 { - if let Some(Ok((bytes, _))) = socket.next_timeout(READ_TIMEOUT).await { - if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) { - if let Some(rendezvous_message::Union::SoftwareUpdate(su)) = msg_in.union { - let version = hbb_common::get_version_from_url(&su.url); - if get_version_number(&version) > get_version_number(crate::VERSION) { - *SOFTWARE_UPDATE_URL.lock().unwrap() = su.url; - } - } - } - } + if get_version_number(&latest_release_version) > get_version_number(crate::VERSION) { + *SOFTWARE_UPDATE_URL.lock().unwrap() = response_url; } Ok(()) } From f41cb0d81c67844786892dd32a28c029a4b0e990 Mon Sep 17 00:00:00 2001 From: Sahil Yeole Date: Wed, 23 Aug 2023 00:14:32 +0530 Subject: [PATCH 30/37] updated get_new_version for ui Signed-off-by: Sahil Yeole --- src/ui_interface.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 7e1b3a9b..6c0d3835 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -594,7 +594,8 @@ pub fn current_is_wayland() -> bool { #[inline] pub fn get_new_version() -> String { - hbb_common::get_version_from_url(&*SOFTWARE_UPDATE_URL.lock().unwrap()) + // hbb_common::get_version_from_url(&*SOFTWARE_UPDATE_URL.lock().unwrap()) + (*SOFTWARE_UPDATE_URL.lock().unwrap().rsplit('/').next().unwrap_or("")).to_string() } #[inline] From d87ea854bc380b6307a9cc2572026ec5024d755b Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 23 Aug 2023 08:15:56 +0800 Subject: [PATCH 31/37] add ColorPicker translations Signed-off-by: 21pages --- flutter/lib/common/widgets/address_book.dart | 7 +++++++ flutter/lib/common/widgets/peer_card.dart | 5 +++-- src/lang/ca.rs | 2 ++ src/lang/cn.rs | 2 ++ src/lang/cs.rs | 2 ++ src/lang/da.rs | 2 ++ src/lang/de.rs | 2 ++ src/lang/el.rs | 2 ++ src/lang/eo.rs | 2 ++ src/lang/es.rs | 2 ++ src/lang/fa.rs | 2 ++ src/lang/fr.rs | 2 ++ src/lang/hu.rs | 2 ++ src/lang/id.rs | 2 ++ src/lang/it.rs | 2 ++ src/lang/ja.rs | 2 ++ src/lang/ko.rs | 2 ++ src/lang/kz.rs | 2 ++ src/lang/lt.rs | 2 ++ src/lang/nl.rs | 2 ++ src/lang/pl.rs | 2 ++ src/lang/pt_PT.rs | 2 ++ src/lang/ptbr.rs | 2 ++ src/lang/ro.rs | 2 ++ src/lang/ru.rs | 2 ++ src/lang/sk.rs | 2 ++ src/lang/sl.rs | 2 ++ src/lang/sq.rs | 2 ++ src/lang/sr.rs | 2 ++ src/lang/sv.rs | 2 ++ src/lang/template.rs | 2 ++ src/lang/th.rs | 2 ++ src/lang/tr.rs | 2 ++ src/lang/tw.rs | 2 ++ src/lang/ua.rs | 2 ++ src/lang/vn.rs | 2 ++ 36 files changed, 78 insertions(+), 2 deletions(-) diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index 292266ae..4af74e31 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -579,6 +579,13 @@ class AddressBookTag extends StatelessWidget { ColorPickerType.accent: false, ColorPickerType.wheel: true, }, + pickerTypeLabels: { + ColorPickerType.primary: translate("Primary Color"), + ColorPickerType.wheel: translate("HSV Color"), + }, + actionButtons: ColorPickerActionButtons( + dialogOkButtonLabel: translate("OK"), + dialogCancelButtonLabel: translate("Cancel")), showColorCode: true, ); if (oldColor != newColor) { diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 74f0dcb3..a9d18b42 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -735,11 +735,12 @@ abstract class BasePeerCard extends StatelessWidget { proc: () async { bool result = gFFI.abModel.changePassword(id, ''); await bind.mainForgetPassword(id: id); + bool toast = false; if (result) { - bool toast = tab == PeerTabIndex.ab; + toast = tab == PeerTabIndex.ab; gFFI.abModel.pushAb(toastIfFail: toast, toastIfSucc: toast); } - showToast(translate('Successful')); + if (!toast) showToast(translate('Successful')); }, padding: menuPadding, dismissOnClicked: true, diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 4801abca..baf6992b 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 3313537d..8c20966c 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", "未成功上传地址簿"), ("synced_peer_readded_tip", "最近会话中存在的设备将会被重新同步到地址簿。"), ("Change Color", "更改颜色"), + ("Primary Color", "基本色"), + ("HSV Color", "HSV 色"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 3d913b0d..0e4d4db6 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", "Nepodařilo se synchronizovat adresář se serverem"), ("synced_peer_readded_tip", "Zařízení, která byla přítomna v posledních relacích, budou synchronizována zpět do adresáře."), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index afaf9838..c5540e75 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index 8eb066f3..c528acce 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", "Synchronisierung des Adressbuchs mit dem Server fehlgeschlagen"), ("synced_peer_readded_tip", "Die Geräte, die in den letzten Sitzungen vorhanden waren, werden erneut zum Adressbuch hinzugefügt."), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/el.rs b/src/lang/el.rs index 56805e57..4e065c80 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index bcb46783..c7712672 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index 5384dfea..7bb24ac5 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", "No se ha podido sincronizar el directorio con el servidor"), ("synced_peer_readded_tip", "Los dispositivos presentes en sesiones recientes se sincronizarán con el directorio."), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index fab82f66..35207642 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 24cc268e..f9263d57 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", "Échec de la synchronisation du carnet d'adresses"), ("synced_peer_readded_tip", "Les appareils qui étaient présents dans les sessions récentes seront synchronisés avec le carnet d'adresses."), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index aaf888d2..72201178 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index a7b12f2e..c8c343c5 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", "Gagal menyinkronkan buku alamat ke server"), ("synced_peer_readded_tip", "Perangkat yang terdaftar dalam sesi-sesi terbaru akan di-sinkronkan kembali ke buku alamat."), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 3b8bd841..22088a06 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", "Impossibile sincronizzare la rubrica con il server"), ("synced_peer_readded_tip", "I dispositivi presenti nelle sessioni recenti saranno sincronizzati di nuovo nella rubrica."), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index 1a9e8da4..b1da5d94 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 28c34f7e..a69b38e2 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 2fb2b8b9..ccca4956 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 6a8c7e9f..873f5909 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 3b08427d..2be2d5da 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 67b9d188..0391dd2f 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 416600ee..becb4c8e 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 98e469f7..704b56d1 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ro.rs b/src/lang/ro.rs index f4d44b3a..2f248262 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 37fdb206..1c43912c 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", "Невозможно синхронизировать адресную книгу с сервером"), ("synced_peer_readded_tip", "Устройства, присутствовавшие в последних сеансах, будут синхронизированы с адресной книгой."), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 8421bd2b..9711586d 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sl.rs b/src/lang/sl.rs index f4c5ff38..f5f4f95f 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index 11e3e1cc..e5d310eb 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 49499947..480964e6 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 4c0f73f5..cf94e706 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index ee7fb0d4..c71a3034 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/th.rs b/src/lang/th.rs index e6f12d85..1ec47d26 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index bb3ab5de..3c3a10da 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 8f28395d..8ef8ca89 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", "未成功上傳地址簿"), ("synced_peer_readded_tip", "最近會話中存在的設備將會被重新同步到地址簿。"), ("Change Color", "更改顏色"), + ("Primary Color", "基本色"), + ("HSV Color", "HSV 色"), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 69ca7c2d..6be8c93b 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 7a0cce35..d83d9ef4 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -539,5 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", ""), ("synced_peer_readded_tip", ""), ("Change Color", ""), + ("Primary Color", ""), + ("HSV Color", ""), ].iter().cloned().collect(); } From f4d120b11f4d9e5c831473f4657c5c8adfe4abaa Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 23 Aug 2023 12:04:19 +0800 Subject: [PATCH 32/37] remove useless line --- src/ui_interface.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ui_interface.rs b/src/ui_interface.rs index 6c0d3835..ed2b4f4f 100644 --- a/src/ui_interface.rs +++ b/src/ui_interface.rs @@ -594,7 +594,6 @@ pub fn current_is_wayland() -> bool { #[inline] pub fn get_new_version() -> String { - // hbb_common::get_version_from_url(&*SOFTWARE_UPDATE_URL.lock().unwrap()) (*SOFTWARE_UPDATE_URL.lock().unwrap().rsplit('/').next().unwrap_or("")).to_string() } From da9fb46b6a65d9c96d15ac4c18cac8fa5be59a5e Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 23 Aug 2023 12:20:19 +0800 Subject: [PATCH 33/37] fix pushAb Signed-off-by: 21pages --- flutter/lib/models/ab_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index e91e42ef..cbb7f747 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -225,7 +225,7 @@ class AbModel { final api = "${await bind.mainGetApiServer()}/api/ab"; var authHeaders = getHttpHeaders(); authHeaders['Content-Type'] = "application/json"; - final body = jsonEncode(_serialize()); + final body = jsonEncode({"data": jsonEncode(_serialize())}); http.Response resp; // support compression if (licensedDevices > 0 && body.length > 1024) { From 28c0e15058298afdbb5b3234266bca2905df7c3d Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 23 Aug 2023 12:56:33 +0800 Subject: [PATCH 34/37] bump to 1.2.3 --- .github/workflows/flutter-build.yml | 2 +- .github/workflows/flutter-tag.yml | 2 +- .github/workflows/history.yml | 2 +- Cargo.lock | 2 +- Cargo.toml | 2 +- appimage/AppImageBuilder-aarch64.yml | 4 ++-- appimage/AppImageBuilder-x86_64.yml | 4 ++-- flatpak/rustdesk.json | 4 ++-- flutter/pubspec.yaml | 2 +- res/PKGBUILD | 2 +- res/rpm-flutter-suse.spec | 2 +- res/rpm-flutter.spec | 2 +- res/rpm.spec | 2 +- 13 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/flutter-build.yml b/.github/workflows/flutter-build.yml index 3ccf4988..69f7df67 100644 --- a/.github/workflows/flutter-build.yml +++ b/.github/workflows/flutter-build.yml @@ -22,7 +22,7 @@ env: # vcpkg version: 2023.04.15 # for multiarch gcc compatibility VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1" - VERSION: "1.2.2" + VERSION: "1.2.3" NDK_VERSION: "r25c" #signing keys env variable checks ANDROID_SIGNING_KEY: '${{ secrets.ANDROID_SIGNING_KEY }}' diff --git a/.github/workflows/flutter-tag.yml b/.github/workflows/flutter-tag.yml index 56ec2c43..4925f26c 100644 --- a/.github/workflows/flutter-tag.yml +++ b/.github/workflows/flutter-tag.yml @@ -15,4 +15,4 @@ jobs: secrets: inherit with: upload-artifact: true - upload-tag: "1.2.2" + upload-tag: "1.2.3" diff --git a/.github/workflows/history.yml b/.github/workflows/history.yml index 6e276162..6921ea4c 100644 --- a/.github/workflows/history.yml +++ b/.github/workflows/history.yml @@ -10,7 +10,7 @@ env: # vcpkg version: 2022.05.10 # for multiarch gcc compatibility VCPKG_COMMIT_ID: "501db0f17ef6df184fcdbfbe0f87cde2313b6ab1" - VERSION: "1.2.2" + VERSION: "1.2.3" jobs: build-for-history-windows: diff --git a/Cargo.lock b/Cargo.lock index 1dc94cfd..99784e40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5124,7 +5124,7 @@ dependencies = [ [[package]] name = "rustdesk" -version = "1.2.2" +version = "1.2.3" dependencies = [ "android_logger", "arboard", diff --git a/Cargo.toml b/Cargo.toml index f1e714e8..2abc198c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustdesk" -version = "1.2.2" +version = "1.2.3" authors = ["rustdesk "] edition = "2021" build= "build.rs" diff --git a/appimage/AppImageBuilder-aarch64.yml b/appimage/AppImageBuilder-aarch64.yml index b372f4eb..337e022b 100644 --- a/appimage/AppImageBuilder-aarch64.yml +++ b/appimage/AppImageBuilder-aarch64.yml @@ -2,7 +2,7 @@ version: 1 script: - rm -rf ./AppDir || true - - bsdtar -zxvf ../rustdesk-1.2.2.deb + - bsdtar -zxvf ../rustdesk-1.2.3.deb - tar -xvf ./data.tar.xz - mkdir ./AppDir - mv ./usr ./AppDir/usr @@ -18,7 +18,7 @@ AppDir: id: rustdesk name: rustdesk icon: rustdesk - version: 1.2.2 + version: 1.2.3 exec: usr/lib/rustdesk/rustdesk exec_args: $@ apt: diff --git a/appimage/AppImageBuilder-x86_64.yml b/appimage/AppImageBuilder-x86_64.yml index 9a4054b6..650d2f20 100644 --- a/appimage/AppImageBuilder-x86_64.yml +++ b/appimage/AppImageBuilder-x86_64.yml @@ -2,7 +2,7 @@ version: 1 script: - rm -rf ./AppDir || true - - bsdtar -zxvf ../rustdesk-1.2.2.deb + - bsdtar -zxvf ../rustdesk-1.2.3.deb - tar -xvf ./data.tar.xz - mkdir ./AppDir - mv ./usr ./AppDir/usr @@ -18,7 +18,7 @@ AppDir: id: rustdesk name: rustdesk icon: rustdesk - version: 1.2.2 + version: 1.2.3 exec: usr/lib/rustdesk/rustdesk exec_args: $@ apt: diff --git a/flatpak/rustdesk.json b/flatpak/rustdesk.json index 4a8334fc..4d2e297c 100644 --- a/flatpak/rustdesk.json +++ b/flatpak/rustdesk.json @@ -12,7 +12,7 @@ "name": "rustdesk", "buildsystem": "simple", "build-commands": [ - "bsdtar -zxvf rustdesk-1.2.2.deb", + "bsdtar -zxvf rustdesk-1.2.3.deb", "tar -xvf ./data.tar.xz", "cp -r ./usr/* /app/", "mkdir -p /app/bin && ln -s /app/lib/rustdesk/rustdesk /app/bin/rustdesk", @@ -26,7 +26,7 @@ "sources": [ { "type": "file", - "path": "../rustdesk-1.2.2.deb" + "path": "../rustdesk-1.2.3.deb" }, { "type": "file", diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 04af8f92..b7141455 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # 1.1.9-1 works for android, but for ios it becomes 1.1.91, need to set it to 1.1.9-a.1 for iOS, will get 1.1.9.1, but iOS store not allow 4 numbers -version: 1.2.2 +version: 1.2.3 environment: sdk: ">=2.17.0" diff --git a/res/PKGBUILD b/res/PKGBUILD index 4d3911b3..0e83b689 100644 --- a/res/PKGBUILD +++ b/res/PKGBUILD @@ -1,5 +1,5 @@ pkgname=rustdesk -pkgver=1.2.2 +pkgver=1.2.3 pkgrel=0 epoch= pkgdesc="" diff --git a/res/rpm-flutter-suse.spec b/res/rpm-flutter-suse.spec index 08080424..8c71fa7d 100644 --- a/res/rpm-flutter-suse.spec +++ b/res/rpm-flutter-suse.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.2.2 +Version: 1.2.3 Release: 0 Summary: RPM package License: GPL-3.0 diff --git a/res/rpm-flutter.spec b/res/rpm-flutter.spec index 5b4899bf..ca63093e 100644 --- a/res/rpm-flutter.spec +++ b/res/rpm-flutter.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.2.2 +Version: 1.2.3 Release: 0 Summary: RPM package License: GPL-3.0 diff --git a/res/rpm.spec b/res/rpm.spec index 6ce788ae..c92ad904 100644 --- a/res/rpm.spec +++ b/res/rpm.spec @@ -1,5 +1,5 @@ Name: rustdesk -Version: 1.2.2 +Version: 1.2.3 Release: 0 Summary: RPM package License: GPL-3.0 From 33cbed592a5821664541c5525b7409b6197291d1 Mon Sep 17 00:00:00 2001 From: SergeyMy <131688106+SergeyMy@users.noreply.github.com> Date: Wed, 23 Aug 2023 12:25:43 +0500 Subject: [PATCH 35/37] Update ru.rs --- src/lang/ru.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 69f4f2f1..cc5e97da 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -539,7 +539,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("push_ab_failed_tip", "Невозможно синхронизировать адресную книгу с сервером"), ("synced_peer_readded_tip", "Устройства, присутствовавшие в последних сеансах, будут синхронизированы с адресной книгой."), ("Change Color", "Изменить цвет"), - ("Primary Color", ""), - ("HSV Color", ""), + ("Primary Color", "Основной цвет"), + ("HSV Color", "HSV цвет"), ].iter().cloned().collect(); } From 8785c08861f602c9f33b5d6ded5c606e92f1fa68 Mon Sep 17 00:00:00 2001 From: Mr-Update <37781396+Mr-Update@users.noreply.github.com> Date: Wed, 23 Aug 2023 14:27:30 +0200 Subject: [PATCH 36/37] Update de.rs --- src/lang/de.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lang/de.rs b/src/lang/de.rs index c528acce..fe1fe864 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -538,8 +538,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("pull_ab_failed_tip", "Aktualisierung des Adressbuchs fehlgeschlagen"), ("push_ab_failed_tip", "Synchronisierung des Adressbuchs mit dem Server fehlgeschlagen"), ("synced_peer_readded_tip", "Die Geräte, die in den letzten Sitzungen vorhanden waren, werden erneut zum Adressbuch hinzugefügt."), - ("Change Color", ""), - ("Primary Color", ""), - ("HSV Color", ""), + ("Change Color", "Farbe ändern"), + ("Primary Color", "Primärfarbe"), + ("HSV Color", "HSV-Farbe"), ].iter().cloned().collect(); } From cb73490107c36f36625b70ae6291df7b56b4dcb7 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 23 Aug 2023 21:59:22 +0800 Subject: [PATCH 37/37] add quick fix for https://github.com/rustdesk/rustdesk/issues/5488#issuecomment-1689997785 --- flutter/lib/desktop/pages/remote_tab_page.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 51bcec51..9c6ed6ce 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -354,7 +354,15 @@ class _ConnectionTabPageState extends State { )); } - if (perms['keyboard'] != false && !ffi.ffiModel.viewOnly) {} + if (perms['keyboard'] != false && !ffi.ffiModel.viewOnly) { + menu.add(RemoteMenuEntry.insertLock(sessionId, padding, + dismissFunc: cancelFunc)); + + if (pi.platform == kPeerPlatformLinux || pi.sasEnabled) { + menu.add(RemoteMenuEntry.insertCtrlAltDel(sessionId, padding, + dismissFunc: cancelFunc)); + } + } menu.addAll([ MenuEntryDivider(),