terminal works basically. (#12189)

* terminal works basically.
todo:
- persistent
- sessions restore
- web
- mobile

* missed terminal persistent option change

* android sdk 34 -> 35

* +#![cfg_attr(lt_1_77, feature(c_str_literals))]

* fixing ci

* fix ci

* fix ci for android

* try "Fix Android SDK Platform 35"

* fix android 34

* revert flutter_plugin_android_lifecycle to 2.0.17 which used in rustdesk 1.4.0

* refactor, but break something of desktop terminal (new tab showing loading)

* fix connecting...
This commit is contained in:
RustDesk
2025-07-01 13:12:55 +08:00
committed by GitHub
parent ee5cdc3155
commit 5faf0ad3cf
130 changed files with 4064 additions and 4247 deletions

View File

@ -30,6 +30,7 @@ import 'common/widgets/overlay.dart';
import 'mobile/pages/file_manager_page.dart';
import 'mobile/pages/remote_page.dart';
import 'mobile/pages/view_camera_page.dart';
import 'mobile/pages/terminal_page.dart';
import 'desktop/pages/remote_page.dart' as desktop_remote;
import 'desktop/pages/file_manager_page.dart' as desktop_file_manager;
import 'desktop/pages/view_camera_page.dart' as desktop_view_camera;
@ -99,6 +100,7 @@ enum DesktopType {
remote,
fileTransfer,
viewCamera,
terminal,
cm,
portForward,
}
@ -1571,7 +1573,9 @@ bool option2bool(String option, String value) {
String bool2option(String option, bool b) {
String res;
if (option.startsWith('enable-') && option != kOptionEnableUdpPunch && option != kOptionEnableIpv6Punch) {
if (option.startsWith('enable-') &&
option != kOptionEnableUdpPunch &&
option != kOptionEnableIpv6Punch) {
res = b ? defaultOptionYes : 'N';
} else if (option.startsWith('allow-') ||
option == kOptionStopService ||
@ -2117,6 +2121,7 @@ enum UriLinkType {
viewCamera,
portForward,
rdp,
terminal,
}
// uri link handler
@ -2181,6 +2186,11 @@ bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
id = args[i + 1];
i++;
break;
case '--terminal':
type = UriLinkType.terminal;
id = args[i + 1];
i++;
break;
case '--password':
password = args[i + 1];
i++;
@ -2230,6 +2240,12 @@ bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
password: password, forceRelay: forceRelay);
});
break;
case UriLinkType.terminal:
Future.delayed(Duration.zero, () {
rustDeskWinManager.newTerminal(id!,
password: password, forceRelay: forceRelay);
});
break;
}
return true;
@ -2247,7 +2263,8 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
"file-transfer",
"view-camera",
"port-forward",
"rdp"
"rdp",
"terminal"
];
if (uri.authority.isEmpty &&
uri.path.split('').every((char) => char == '/')) {
@ -2276,21 +2293,10 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
}
}
} else if (options.contains(uri.authority)) {
final optionIndex = options.indexOf(uri.authority);
command = '--${uri.authority}';
if (uri.path.length > 1) {
id = uri.path.substring(1);
}
if (isMobile && id != null) {
if (optionIndex == 0 || optionIndex == 1) {
connect(Get.context!, id);
} else if (optionIndex == 2) {
connect(Get.context!, id, isFileTransfer: true);
} else if (optionIndex == 3) {
connect(Get.context!, id, isViewCamera: true);
}
return null;
}
} else if (uri.authority.length > 2 &&
(uri.path.length <= 1 ||
(uri.path == '/r' || uri.path.startsWith('/r@')))) {
@ -2314,13 +2320,25 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
}
}
if (isMobile) {
if (id != null) {
final forceRelay = queryParameters["relay"] != null;
if (isMobile && id != null) {
final forceRelay = queryParameters["relay"] != null;
final password = queryParameters["password"];
// Determine connection type based on command
if (command == '--file-transfer') {
connect(Get.context!, id,
forceRelay: forceRelay, password: queryParameters["password"]);
return null;
isFileTransfer: true, forceRelay: forceRelay, password: password);
} else if (command == '--view-camera') {
connect(Get.context!, id,
isViewCamera: true, forceRelay: forceRelay, password: password);
} else if (command == '--terminal') {
connect(Get.context!, id,
isTerminal: true, forceRelay: forceRelay, password: password);
} else {
// Default to remote desktop for '--connect', '--play', or direct connection
connect(Get.context!, id, forceRelay: forceRelay, password: password);
}
return null;
}
List<String> args = List.empty(growable: true);
@ -2342,6 +2360,7 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
connectMainDesktop(String id,
{required bool isFileTransfer,
required bool isViewCamera,
required bool isTerminal,
required bool isTcpTunneling,
required bool isRDP,
bool? forceRelay,
@ -2366,6 +2385,12 @@ connectMainDesktop(String id,
isSharedPassword: isSharedPassword,
connToken: connToken,
forceRelay: forceRelay);
} else if (isTerminal) {
await rustDeskWinManager.newTerminal(id,
password: password,
isSharedPassword: isSharedPassword,
connToken: connToken,
forceRelay: forceRelay);
} else {
await rustDeskWinManager.newRemoteDesktop(id,
password: password,
@ -2382,6 +2407,7 @@ connectMainDesktop(String id,
connect(BuildContext context, String id,
{bool isFileTransfer = false,
bool isViewCamera = false,
bool isTerminal = false,
bool isTcpTunneling = false,
bool isRDP = false,
bool forceRelay = false,
@ -2404,7 +2430,7 @@ connect(BuildContext context, String id,
id = id.replaceAll(' ', '');
final oldId = id;
id = await bind.mainHandleRelayId(id: id);
final forceRelay2 = id != oldId || forceRelay;
forceRelay = id != oldId || forceRelay;
assert(!(isFileTransfer && isTcpTunneling && isRDP),
"more than one connect type");
@ -2414,17 +2440,19 @@ connect(BuildContext context, String id,
id,
isFileTransfer: isFileTransfer,
isViewCamera: isViewCamera,
isTerminal: isTerminal,
isTcpTunneling: isTcpTunneling,
isRDP: isRDP,
password: password,
isSharedPassword: isSharedPassword,
forceRelay: forceRelay2,
forceRelay: forceRelay,
);
} else {
await rustDeskWinManager.call(WindowType.Main, kWindowConnect, {
'id': id,
'isFileTransfer': isFileTransfer,
'isViewCamera': isViewCamera,
'isTerminal': isTerminal,
'isTcpTunneling': isTcpTunneling,
'isRDP': isRDP,
'password': password,
@ -2458,7 +2486,10 @@ connect(BuildContext context, String id,
context,
MaterialPageRoute(
builder: (BuildContext context) => FileManagerPage(
id: id, password: password, isSharedPassword: isSharedPassword),
id: id,
password: password,
isSharedPassword: isSharedPassword,
forceRelay: forceRelay),
),
);
}
@ -2473,7 +2504,6 @@ connect(BuildContext context, String id,
id: id,
toolbarState: ToolbarState(),
password: password,
forceRelay: forceRelay,
isSharedPassword: isSharedPassword,
),
),
@ -2483,10 +2513,25 @@ connect(BuildContext context, String id,
context,
MaterialPageRoute(
builder: (BuildContext context) => ViewCameraPage(
id: id, password: password, isSharedPassword: isSharedPassword),
id: id,
password: password,
isSharedPassword: isSharedPassword,
forceRelay: forceRelay),
),
);
}
} else if (isTerminal) {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => TerminalPage(
id: id,
password: password,
isSharedPassword: isSharedPassword,
forceRelay: forceRelay,
),
),
);
} else {
if (isWeb) {
Navigator.push(
@ -2497,7 +2542,6 @@ connect(BuildContext context, String id,
id: id,
toolbarState: ToolbarState(),
password: password,
forceRelay: forceRelay,
isSharedPassword: isSharedPassword,
),
),
@ -2507,7 +2551,10 @@ connect(BuildContext context, String id,
context,
MaterialPageRoute(
builder: (BuildContext context) => RemotePage(
id: id, password: password, isSharedPassword: isSharedPassword),
id: id,
password: password,
isSharedPassword: isSharedPassword,
forceRelay: forceRelay),
),
);
}