Merge pull request #1409 from 21pages/remote-modification
option remote modification
This commit is contained in:
		
						commit
						1e07a604e8
					
				| @ -664,8 +664,6 @@ Future<void> initGlobalFFI() async { | |||||||
|   debugPrint("_globalFFI init end"); |   debugPrint("_globalFFI init end"); | ||||||
|   // after `put`, can also be globally found by Get.find<FFI>(); |   // after `put`, can also be globally found by Get.find<FFI>(); | ||||||
|   Get.put(_globalFFI, permanent: true); |   Get.put(_globalFFI, permanent: true); | ||||||
|   // trigger connection status updater |  | ||||||
|   await bind.mainCheckConnectStatus(); |  | ||||||
|   // global shared preference |   // global shared preference | ||||||
|   await Get.putAsync(() => SharedPreferences.getInstance()); |   await Get.putAsync(() => SharedPreferences.getInstance()); | ||||||
| } | } | ||||||
|  | |||||||
| @ -233,7 +233,6 @@ class _ConnectionPageState extends State<ConnectionPage> { | |||||||
|                         }, |                         }, | ||||||
|                         child: Container( |                         child: Container( | ||||||
|                           height: 24, |                           height: 24, | ||||||
|                           width: 72, |  | ||||||
|                           alignment: Alignment.center, |                           alignment: Alignment.center, | ||||||
|                           decoration: BoxDecoration( |                           decoration: BoxDecoration( | ||||||
|                             color: ftPressed.value |                             color: ftPressed.value | ||||||
| @ -257,7 +256,7 @@ class _ConnectionPageState extends State<ConnectionPage> { | |||||||
|                                 color: ftPressed.value |                                 color: ftPressed.value | ||||||
|                                     ? MyTheme.color(context).bg |                                     ? MyTheme.color(context).bg | ||||||
|                                     : MyTheme.color(context).text), |                                     : MyTheme.color(context).text), | ||||||
|                           ), |                           ).marginSymmetric(horizontal: 12), | ||||||
|                         ), |                         ), | ||||||
|                       )), |                       )), | ||||||
|                   SizedBox( |                   SizedBox( | ||||||
| @ -272,7 +271,6 @@ class _ConnectionPageState extends State<ConnectionPage> { | |||||||
|                       onTap: onConnect, |                       onTap: onConnect, | ||||||
|                       child: Container( |                       child: Container( | ||||||
|                         height: 24, |                         height: 24, | ||||||
|                         width: 65, |  | ||||||
|                         decoration: BoxDecoration( |                         decoration: BoxDecoration( | ||||||
|                           color: connPressed.value |                           color: connPressed.value | ||||||
|                               ? MyTheme.accent |                               ? MyTheme.accent | ||||||
| @ -289,12 +287,12 @@ class _ConnectionPageState extends State<ConnectionPage> { | |||||||
|                         child: Center( |                         child: Center( | ||||||
|                           child: Text( |                           child: Text( | ||||||
|                             translate( |                             translate( | ||||||
|                               "Connection", |                               "Connect", | ||||||
|                             ), |                             ), | ||||||
|                             style: TextStyle( |                             style: TextStyle( | ||||||
|                                 fontSize: 12, color: MyTheme.color(context).bg), |                                 fontSize: 12, color: MyTheme.color(context).bg), | ||||||
|                           ), |                           ), | ||||||
|                         ), |                         ).marginSymmetric(horizontal: 12), | ||||||
|                       ), |                       ), | ||||||
|                     ), |                     ), | ||||||
|                   ), |                   ), | ||||||
|  | |||||||
| @ -93,7 +93,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> { | |||||||
|                 body: Obx(() => DesktopTab( |                 body: Obx(() => DesktopTab( | ||||||
|                       controller: tabController, |                       controller: tabController, | ||||||
|                       theme: theme, |                       theme: theme, | ||||||
|                       isMainWindow: false, |                       tabType: DesktopTabType.remoteScreen, | ||||||
|                       showTabBar: fullscreen.isFalse, |                       showTabBar: fullscreen.isFalse, | ||||||
|                       onClose: () { |                       onClose: () { | ||||||
|                         tabController.clear(); |                         tabController.clear(); | ||||||
|  | |||||||
| @ -46,7 +46,7 @@ class _DesktopTabPageState extends State<DesktopTabPage> { | |||||||
|                 body: DesktopTab( |                 body: DesktopTab( | ||||||
|                   controller: tabController, |                   controller: tabController, | ||||||
|                   theme: dark ? TarBarTheme.dark() : TarBarTheme.light(), |                   theme: dark ? TarBarTheme.dark() : TarBarTheme.light(), | ||||||
|                   isMainWindow: true, |                   tabType: DesktopTabType.main, | ||||||
|                   tail: ActionIcon( |                   tail: ActionIcon( | ||||||
|                     message: 'Settings', |                     message: 'Settings', | ||||||
|                     icon: IconFont.menu, |                     icon: IconFont.menu, | ||||||
|  | |||||||
| @ -74,7 +74,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> { | |||||||
|             body: DesktopTab( |             body: DesktopTab( | ||||||
|               controller: tabController, |               controller: tabController, | ||||||
|               theme: theme, |               theme: theme, | ||||||
|               isMainWindow: false, |               tabType: DesktopTabType.fileTransfer, | ||||||
|               onClose: () { |               onClose: () { | ||||||
|                 tabController.clear(); |                 tabController.clear(); | ||||||
|               }, |               }, | ||||||
|  | |||||||
| @ -19,11 +19,13 @@ class PortForwardTabPage extends StatefulWidget { | |||||||
| 
 | 
 | ||||||
| class _PortForwardTabPageState extends State<PortForwardTabPage> { | class _PortForwardTabPageState extends State<PortForwardTabPage> { | ||||||
|   final tabController = Get.put(DesktopTabController()); |   final tabController = Get.put(DesktopTabController()); | ||||||
|  |   late final bool isRDP; | ||||||
| 
 | 
 | ||||||
|   static final IconData selectedIcon = Icons.forward_sharp; |   static const IconData selectedIcon = Icons.forward_sharp; | ||||||
|   static final IconData unselectedIcon = Icons.forward_outlined; |   static const IconData unselectedIcon = Icons.forward_outlined; | ||||||
| 
 | 
 | ||||||
|   _PortForwardTabPageState(Map<String, dynamic> params) { |   _PortForwardTabPageState(Map<String, dynamic> params) { | ||||||
|  |     isRDP = params['isRDP']; | ||||||
|     tabController.add(TabInfo( |     tabController.add(TabInfo( | ||||||
|         key: params['id'], |         key: params['id'], | ||||||
|         label: params['id'], |         label: params['id'], | ||||||
| @ -32,7 +34,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> { | |||||||
|         page: PortForwardPage( |         page: PortForwardPage( | ||||||
|           key: ValueKey(params['id']), |           key: ValueKey(params['id']), | ||||||
|           id: params['id'], |           id: params['id'], | ||||||
|           isRDP: params['isRDP'], |           isRDP: isRDP, | ||||||
|         ))); |         ))); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -76,7 +78,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> { | |||||||
|             body: DesktopTab( |             body: DesktopTab( | ||||||
|               controller: tabController, |               controller: tabController, | ||||||
|               theme: theme, |               theme: theme, | ||||||
|               isMainWindow: false, |               tabType: isRDP ? DesktopTabType.rdp : DesktopTabType.portForward, | ||||||
|               onClose: () { |               onClose: () { | ||||||
|                 tabController.clear(); |                 tabController.clear(); | ||||||
|               }, |               }, | ||||||
|  | |||||||
| @ -111,7 +111,7 @@ class ConnectionManagerState extends State<ConnectionManager> { | |||||||
|             showMaximize: false, |             showMaximize: false, | ||||||
|             showMinimize: false, |             showMinimize: false, | ||||||
|             controller: serverModel.tabController, |             controller: serverModel.tabController, | ||||||
|             isMainWindow: true, |             tabType: DesktopTabType.cm, | ||||||
|             pageViewBuilder: (pageView) => Row(children: [ |             pageViewBuilder: (pageView) => Row(children: [ | ||||||
|                   Expanded(child: pageView), |                   Expanded(child: pageView), | ||||||
|                   Consumer<ChatModel>( |                   Consumer<ChatModel>( | ||||||
| @ -294,7 +294,8 @@ class _CmHeaderState extends State<_CmHeader> | |||||||
|         Offstage( |         Offstage( | ||||||
|           offstage: client.isFileTransfer, |           offstage: client.isFileTransfer, | ||||||
|           child: IconButton( |           child: IconButton( | ||||||
|             onPressed: () => gFFI.chatModel.toggleCMChatPage(client.id), |             onPressed: () => checkClickTime( | ||||||
|  |                 client.id, () => gFFI.chatModel.toggleCMChatPage(client.id)), | ||||||
|             icon: Icon(Icons.message_outlined), |             icon: Icon(Icons.message_outlined), | ||||||
|           ), |           ), | ||||||
|         ) |         ) | ||||||
| @ -326,7 +327,8 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> { | |||||||
|             BoxDecoration(color: enabled ? MyTheme.accent80 : Colors.grey), |             BoxDecoration(color: enabled ? MyTheme.accent80 : Colors.grey), | ||||||
|         padding: EdgeInsets.all(4.0), |         padding: EdgeInsets.all(4.0), | ||||||
|         child: InkWell( |         child: InkWell( | ||||||
|           onTap: () => onTap?.call(!enabled), |           onTap: () => | ||||||
|  |               checkClickTime(widget.client.id, () => onTap?.call(!enabled)), | ||||||
|           child: Image( |           child: Image( | ||||||
|             image: icon, |             image: icon, | ||||||
|             width: 50, |             width: 50, | ||||||
| @ -422,7 +424,8 @@ class _CmControlPanel extends StatelessWidget { | |||||||
|           decoration: BoxDecoration( |           decoration: BoxDecoration( | ||||||
|               color: Colors.redAccent, borderRadius: BorderRadius.circular(10)), |               color: Colors.redAccent, borderRadius: BorderRadius.circular(10)), | ||||||
|           child: InkWell( |           child: InkWell( | ||||||
|               onTap: () => handleDisconnect(context), |               onTap: () => | ||||||
|  |                   checkClickTime(client.id, () => handleDisconnect(context)), | ||||||
|               child: Row( |               child: Row( | ||||||
|                 mainAxisAlignment: MainAxisAlignment.center, |                 mainAxisAlignment: MainAxisAlignment.center, | ||||||
|                 children: [ |                 children: [ | ||||||
| @ -447,7 +450,8 @@ class _CmControlPanel extends StatelessWidget { | |||||||
|           decoration: BoxDecoration( |           decoration: BoxDecoration( | ||||||
|               color: MyTheme.accent, borderRadius: BorderRadius.circular(10)), |               color: MyTheme.accent, borderRadius: BorderRadius.circular(10)), | ||||||
|           child: InkWell( |           child: InkWell( | ||||||
|               onTap: () => handleAccept(context), |               onTap: () => | ||||||
|  |                   checkClickTime(client.id, () => handleAccept(context)), | ||||||
|               child: Row( |               child: Row( | ||||||
|                 mainAxisAlignment: MainAxisAlignment.center, |                 mainAxisAlignment: MainAxisAlignment.center, | ||||||
|                 children: [ |                 children: [ | ||||||
| @ -469,7 +473,8 @@ class _CmControlPanel extends StatelessWidget { | |||||||
|               borderRadius: BorderRadius.circular(10), |               borderRadius: BorderRadius.circular(10), | ||||||
|               border: Border.all(color: Colors.grey)), |               border: Border.all(color: Colors.grey)), | ||||||
|           child: InkWell( |           child: InkWell( | ||||||
|               onTap: () => handleDisconnect(context), |               onTap: () => | ||||||
|  |                   checkClickTime(client.id, () => handleDisconnect(context)), | ||||||
|               child: Row( |               child: Row( | ||||||
|                 mainAxisAlignment: MainAxisAlignment.center, |                 mainAxisAlignment: MainAxisAlignment.center, | ||||||
|                 children: [ |                 children: [ | ||||||
| @ -572,3 +577,12 @@ Widget clientInfo(Client client) { | |||||||
|         ), |         ), | ||||||
|       ])); |       ])); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void checkClickTime(int id, Function() callback) async { | ||||||
|  |   var clickCallbackTime = DateTime.now().millisecondsSinceEpoch; | ||||||
|  |   await bind.cmCheckClickTime(connId: id); | ||||||
|  |   Timer(const Duration(milliseconds: 120), () async { | ||||||
|  |     var d = clickCallbackTime - await bind.cmGetClickTime(); | ||||||
|  |     if (d > 120) callback(); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| import 'dart:io'; | import 'dart:io'; | ||||||
|  | import 'dart:async'; | ||||||
| import 'dart:math'; | import 'dart:math'; | ||||||
| 
 | 
 | ||||||
| import 'package:desktop_multi_window/desktop_multi_window.dart'; | import 'package:desktop_multi_window/desktop_multi_window.dart'; | ||||||
| @ -6,6 +7,7 @@ import 'package:flutter/material.dart'; | |||||||
| import 'package:flutter_hbb/common.dart'; | import 'package:flutter_hbb/common.dart'; | ||||||
| import 'package:flutter_hbb/consts.dart'; | import 'package:flutter_hbb/consts.dart'; | ||||||
| import 'package:flutter_hbb/main.dart'; | import 'package:flutter_hbb/main.dart'; | ||||||
|  | import 'package:flutter_hbb/models/platform_model.dart'; | ||||||
| import 'package:get/get.dart'; | import 'package:get/get.dart'; | ||||||
| import 'package:scroll_pos/scroll_pos.dart'; | import 'package:scroll_pos/scroll_pos.dart'; | ||||||
| import 'package:window_manager/window_manager.dart'; | import 'package:window_manager/window_manager.dart'; | ||||||
| @ -34,6 +36,15 @@ class TabInfo { | |||||||
|       required this.page}); |       required this.page}); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | enum DesktopTabType { | ||||||
|  |   main, | ||||||
|  |   cm, | ||||||
|  |   remoteScreen, | ||||||
|  |   fileTransfer, | ||||||
|  |   portForward, | ||||||
|  |   rdp, | ||||||
|  | } | ||||||
|  | 
 | ||||||
| class DesktopTabState { | class DesktopTabState { | ||||||
|   final List<TabInfo> tabs = []; |   final List<TabInfo> tabs = []; | ||||||
|   final ScrollPosController scrollController = |   final ScrollPosController scrollController = | ||||||
| @ -64,6 +75,7 @@ class DesktopTabController { | |||||||
|       state.update((val) { |       state.update((val) { | ||||||
|         val!.tabs.add(tab); |         val!.tabs.add(tab); | ||||||
|       }); |       }); | ||||||
|  |       state.value.scrollController.itemCount = state.value.tabs.length; | ||||||
|       toIndex = state.value.tabs.length - 1; |       toIndex = state.value.tabs.length - 1; | ||||||
|       assert(toIndex >= 0); |       assert(toIndex >= 0); | ||||||
|     } |     } | ||||||
| @ -96,8 +108,16 @@ class DesktopTabController { | |||||||
|   void jumpTo(int index) { |   void jumpTo(int index) { | ||||||
|     state.update((val) { |     state.update((val) { | ||||||
|       val!.selected = index; |       val!.selected = index; | ||||||
|  |       Future.delayed(Duration.zero, (() { | ||||||
|  |         if (val.pageController.hasClients) { | ||||||
|           val.pageController.jumpToPage(index); |           val.pageController.jumpToPage(index); | ||||||
|  |         } | ||||||
|  |         if (val.scrollController.hasClients && | ||||||
|  |             val.scrollController.canScroll && | ||||||
|  |             val.scrollController.itemCount >= index) { | ||||||
|           val.scrollController.scrollToItem(index, center: true, animate: true); |           val.scrollController.scrollToItem(index, center: true, animate: true); | ||||||
|  |         } | ||||||
|  |       })); | ||||||
|     }); |     }); | ||||||
|     onSelected?.call(index); |     onSelected?.call(index); | ||||||
|   } |   } | ||||||
| @ -134,6 +154,7 @@ typedef LabelGetter = Rx<String> Function(String key); | |||||||
| class DesktopTab extends StatelessWidget { | class DesktopTab extends StatelessWidget { | ||||||
|   final Function(String)? onTabClose; |   final Function(String)? onTabClose; | ||||||
|   final TarBarTheme theme; |   final TarBarTheme theme; | ||||||
|  |   final DesktopTabType tabType; | ||||||
|   final bool isMainWindow; |   final bool isMainWindow; | ||||||
|   final bool showTabBar; |   final bool showTabBar; | ||||||
|   final bool showLogo; |   final bool showLogo; | ||||||
| @ -152,7 +173,7 @@ class DesktopTab extends StatelessWidget { | |||||||
| 
 | 
 | ||||||
|   const DesktopTab({ |   const DesktopTab({ | ||||||
|     required this.controller, |     required this.controller, | ||||||
|     required this.isMainWindow, |     required this.tabType, | ||||||
|     this.theme = const TarBarTheme.light(), |     this.theme = const TarBarTheme.light(), | ||||||
|     this.onTabClose, |     this.onTabClose, | ||||||
|     this.showTabBar = true, |     this.showTabBar = true, | ||||||
| @ -166,7 +187,8 @@ class DesktopTab extends StatelessWidget { | |||||||
|     this.onClose, |     this.onClose, | ||||||
|     this.tabBuilder, |     this.tabBuilder, | ||||||
|     this.labelGetter, |     this.labelGetter, | ||||||
|   }); |   }) : isMainWindow = | ||||||
|  |             tabType == DesktopTabType.main || tabType == DesktopTabType.cm; | ||||||
| 
 | 
 | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
| @ -195,11 +217,48 @@ class DesktopTab extends StatelessWidget { | |||||||
|     ]); |     ]); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   Widget _buildBlock({required Widget child}) { | ||||||
|  |     if (tabType != DesktopTabType.main) { | ||||||
|  |       return child; | ||||||
|  |     } | ||||||
|  |     var block = false.obs; | ||||||
|  |     return Obx(() => MouseRegion( | ||||||
|  |           onEnter: (_) async { | ||||||
|  |             if (!option2bool( | ||||||
|  |                 'allow-remote-config-modification', | ||||||
|  |                 await bind.mainGetOption( | ||||||
|  |                     key: 'allow-remote-config-modification'))) { | ||||||
|  |               var time0 = DateTime.now().millisecondsSinceEpoch; | ||||||
|  |               await bind.mainCheckMouseTime(); | ||||||
|  |               Timer(const Duration(milliseconds: 120), () async { | ||||||
|  |                 var d = time0 - await bind.mainGetMouseTime(); | ||||||
|  |                 if (d < 120) { | ||||||
|  |                   block.value = true; | ||||||
|  |                 } | ||||||
|  |               }); | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           onExit: (_) => block.value = false, | ||||||
|  |           child: Stack( | ||||||
|  |             children: [ | ||||||
|  |               child, | ||||||
|  |               Offstage( | ||||||
|  |                   offstage: !block.value, | ||||||
|  |                   child: Container( | ||||||
|  |                     color: Colors.black.withOpacity(0.5), | ||||||
|  |                   )), | ||||||
|  |             ], | ||||||
|  |           ), | ||||||
|  |         )); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   Widget _buildPageView() { |   Widget _buildPageView() { | ||||||
|     return Obx(() => PageView( |     return _buildBlock( | ||||||
|  |         child: Obx(() => PageView( | ||||||
|             controller: state.value.pageController, |             controller: state.value.pageController, | ||||||
|         children: |             children: state.value.tabs | ||||||
|             state.value.tabs.map((tab) => tab.page).toList(growable: false))); |                 .map((tab) => tab.page) | ||||||
|  |                 .toList(growable: false)))); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Widget _buildBar() { |   Widget _buildBar() { | ||||||
|  | |||||||
| @ -81,6 +81,8 @@ Future<void> initEnv(String appType) async { | |||||||
| 
 | 
 | ||||||
| void runMainApp(bool startService) async { | void runMainApp(bool startService) async { | ||||||
|   await initEnv(kAppTypeMain); |   await initEnv(kAppTypeMain); | ||||||
|  |   // trigger connection status updater | ||||||
|  |   await bind.mainCheckConnectStatus(); | ||||||
|   if (startService) { |   if (startService) { | ||||||
|     // await windowManager.ensureInitialized(); |     // await windowManager.ensureInitialized(); | ||||||
|     // disable tray |     // disable tray | ||||||
| @ -89,7 +91,8 @@ void runMainApp(bool startService) async { | |||||||
|   } |   } | ||||||
|   runApp(App()); |   runApp(App()); | ||||||
|   // set window option |   // set window option | ||||||
|   WindowOptions windowOptions = getHiddenTitleBarWindowOptions(const Size(1280, 720)); |   WindowOptions windowOptions = | ||||||
|  |       getHiddenTitleBarWindowOptions(const Size(1280, 720)); | ||||||
|   windowManager.waitUntilReadyToShow(windowOptions, () async { |   windowManager.waitUntilReadyToShow(windowOptions, () async { | ||||||
|     await windowManager.show(); |     await windowManager.show(); | ||||||
|     await windowManager.focus(); |     await windowManager.focus(); | ||||||
|  | |||||||
| @ -22,12 +22,13 @@ use crate::ui_interface; | |||||||
| #[cfg(not(any(target_os = "android", target_os = "ios")))] | #[cfg(not(any(target_os = "android", target_os = "ios")))] | ||||||
| use crate::ui_interface::{change_id, check_connect_status, is_ok_change_id}; | use crate::ui_interface::{change_id, check_connect_status, is_ok_change_id}; | ||||||
| use crate::ui_interface::{ | use crate::ui_interface::{ | ||||||
|     check_super_user_permission, discover, forget_password, get_api_server, get_app_name, |     check_mouse_time, check_super_user_permission, discover, forget_password, get_api_server, | ||||||
|     get_async_job_status, get_connect_status, get_fav, get_id, get_lan_peers, get_langs, |     get_app_name, get_async_job_status, get_connect_status, get_fav, get_id, get_lan_peers, | ||||||
|     get_license, get_local_option, get_option, get_options, get_peer, get_peer_option, get_socks, |     get_langs, get_license, get_local_option, get_mouse_time, get_option, get_options, get_peer, | ||||||
|     get_sound_inputs, get_uuid, get_version, has_hwcodec, has_rendezvous_service, post_request, |     get_peer_option, get_socks, get_sound_inputs, get_uuid, get_version, has_hwcodec, | ||||||
|     set_local_option, set_option, set_options, set_peer_option, set_permanent_password, set_socks, |     has_rendezvous_service, post_request, set_local_option, set_option, set_options, | ||||||
|     store_fav, test_if_valid_server, update_temporary_password, using_public_server, |     set_peer_option, set_permanent_password, set_socks, store_fav, test_if_valid_server, | ||||||
|  |     update_temporary_password, using_public_server, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| fn initialize(app_dir: &str) { | fn initialize(app_dir: &str) { | ||||||
| @ -472,7 +473,7 @@ pub fn main_get_connect_status() -> String { | |||||||
| 
 | 
 | ||||||
| pub fn main_check_connect_status() { | pub fn main_check_connect_status() { | ||||||
|     #[cfg(not(any(target_os = "android", target_os = "ios")))] |     #[cfg(not(any(target_os = "android", target_os = "ios")))] | ||||||
|     check_connect_status(true); |     check_mouse_time(); // avoid multi calls
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn main_is_using_public_server() -> bool { | pub fn main_is_using_public_server() -> bool { | ||||||
| @ -764,6 +765,14 @@ pub fn main_check_super_user_permission() -> bool { | |||||||
|     check_super_user_permission() |     check_super_user_permission() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | pub fn main_check_mouse_time() { | ||||||
|  |     check_mouse_time(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn main_get_mouse_time() -> f64 { | ||||||
|  |     get_mouse_time() | ||||||
|  | } | ||||||
|  | 
 | ||||||
| pub fn cm_send_chat(conn_id: i32, msg: String) { | pub fn cm_send_chat(conn_id: i32, msg: String) { | ||||||
|     connection_manager::send_chat(conn_id, msg); |     connection_manager::send_chat(conn_id, msg); | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user