input_model.dart 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'dart:math';
  5. import 'dart:ui' as ui;
  6. import 'package:desktop_multi_window/desktop_multi_window.dart';
  7. import 'package:flutter/gestures.dart';
  8. import 'package:flutter/services.dart';
  9. import 'package:flutter/widgets.dart';
  10. import 'package:flutter_hbb/main.dart';
  11. import 'package:flutter_hbb/utils/multi_window_manager.dart';
  12. import 'package:get/get.dart';
  13. import '../../models/model.dart';
  14. import '../../models/platform_model.dart';
  15. import '../common.dart';
  16. import '../consts.dart';
  17. /// Mouse button enum.
  18. enum MouseButtons { left, right, wheel, back }
  19. const _kMouseEventDown = 'mousedown';
  20. const _kMouseEventUp = 'mouseup';
  21. const _kMouseEventMove = 'mousemove';
  22. class CanvasCoords {
  23. double x = 0;
  24. double y = 0;
  25. double scale = 1.0;
  26. double scrollX = 0;
  27. double scrollY = 0;
  28. ScrollStyle scrollStyle = ScrollStyle.scrollauto;
  29. Size size = Size.zero;
  30. CanvasCoords();
  31. Map<String, dynamic> toJson() {
  32. return {
  33. 'x': x,
  34. 'y': y,
  35. 'scale': scale,
  36. 'scrollX': scrollX,
  37. 'scrollY': scrollY,
  38. 'scrollStyle':
  39. scrollStyle == ScrollStyle.scrollauto ? 'scrollauto' : 'scrollbar',
  40. 'size': {
  41. 'w': size.width,
  42. 'h': size.height,
  43. }
  44. };
  45. }
  46. static CanvasCoords fromJson(Map<String, dynamic> json) {
  47. final model = CanvasCoords();
  48. model.x = json['x'];
  49. model.y = json['y'];
  50. model.scale = json['scale'];
  51. model.scrollX = json['scrollX'];
  52. model.scrollY = json['scrollY'];
  53. model.scrollStyle = json['scrollStyle'] == 'scrollauto'
  54. ? ScrollStyle.scrollauto
  55. : ScrollStyle.scrollbar;
  56. model.size = Size(json['size']['w'], json['size']['h']);
  57. return model;
  58. }
  59. static CanvasCoords fromCanvasModel(CanvasModel model) {
  60. final coords = CanvasCoords();
  61. coords.x = model.x;
  62. coords.y = model.y;
  63. coords.scale = model.scale;
  64. coords.scrollX = model.scrollX;
  65. coords.scrollY = model.scrollY;
  66. coords.scrollStyle = model.scrollStyle;
  67. coords.size = model.size;
  68. return coords;
  69. }
  70. }
  71. class CursorCoords {
  72. Offset offset = Offset.zero;
  73. CursorCoords();
  74. Map<String, dynamic> toJson() {
  75. return {
  76. 'offset_x': offset.dx,
  77. 'offset_y': offset.dy,
  78. };
  79. }
  80. static CursorCoords fromJson(Map<String, dynamic> json) {
  81. final model = CursorCoords();
  82. model.offset = Offset(json['offset_x'], json['offset_y']);
  83. return model;
  84. }
  85. static CursorCoords fromCursorModel(CursorModel model) {
  86. final coords = CursorCoords();
  87. coords.offset = model.offset;
  88. return coords;
  89. }
  90. }
  91. class RemoteWindowCoords {
  92. RemoteWindowCoords(
  93. this.windowRect, this.canvas, this.cursor, this.remoteRect);
  94. Rect windowRect;
  95. CanvasCoords canvas;
  96. CursorCoords cursor;
  97. Rect remoteRect;
  98. Offset relativeOffset = Offset.zero;
  99. Map<String, dynamic> toJson() {
  100. return {
  101. 'canvas': canvas.toJson(),
  102. 'cursor': cursor.toJson(),
  103. 'windowRect': rectToJson(windowRect),
  104. 'remoteRect': rectToJson(remoteRect),
  105. };
  106. }
  107. static Map<String, dynamic> rectToJson(Rect r) {
  108. return {
  109. 'l': r.left,
  110. 't': r.top,
  111. 'w': r.width,
  112. 'h': r.height,
  113. };
  114. }
  115. static Rect rectFromJson(Map<String, dynamic> json) {
  116. return Rect.fromLTWH(
  117. json['l'],
  118. json['t'],
  119. json['w'],
  120. json['h'],
  121. );
  122. }
  123. RemoteWindowCoords.fromJson(Map<String, dynamic> json)
  124. : windowRect = rectFromJson(json['windowRect']),
  125. canvas = CanvasCoords.fromJson(json['canvas']),
  126. cursor = CursorCoords.fromJson(json['cursor']),
  127. remoteRect = rectFromJson(json['remoteRect']);
  128. }
  129. extension ToString on MouseButtons {
  130. String get value {
  131. switch (this) {
  132. case MouseButtons.left:
  133. return 'left';
  134. case MouseButtons.right:
  135. return 'right';
  136. case MouseButtons.wheel:
  137. return 'wheel';
  138. case MouseButtons.back:
  139. return 'back';
  140. }
  141. }
  142. }
  143. class PointerEventToRust {
  144. final String kind;
  145. final String type;
  146. final dynamic value;
  147. PointerEventToRust(this.kind, this.type, this.value);
  148. Map<String, dynamic> toJson() {
  149. return {
  150. 'k': kind,
  151. 'v': {
  152. 't': type,
  153. 'v': value,
  154. }
  155. };
  156. }
  157. }
  158. class ToReleaseRawKeys {
  159. RawKeyEvent? lastLShiftKeyEvent;
  160. RawKeyEvent? lastRShiftKeyEvent;
  161. RawKeyEvent? lastLCtrlKeyEvent;
  162. RawKeyEvent? lastRCtrlKeyEvent;
  163. RawKeyEvent? lastLAltKeyEvent;
  164. RawKeyEvent? lastRAltKeyEvent;
  165. RawKeyEvent? lastLCommandKeyEvent;
  166. RawKeyEvent? lastRCommandKeyEvent;
  167. RawKeyEvent? lastSuperKeyEvent;
  168. reset() {
  169. lastLShiftKeyEvent = null;
  170. lastRShiftKeyEvent = null;
  171. lastLCtrlKeyEvent = null;
  172. lastRCtrlKeyEvent = null;
  173. lastLAltKeyEvent = null;
  174. lastRAltKeyEvent = null;
  175. lastLCommandKeyEvent = null;
  176. lastRCommandKeyEvent = null;
  177. lastSuperKeyEvent = null;
  178. }
  179. updateKeyDown(LogicalKeyboardKey logicKey, RawKeyDownEvent e) {
  180. if (e.isAltPressed) {
  181. if (logicKey == LogicalKeyboardKey.altLeft) {
  182. lastLAltKeyEvent = e;
  183. } else if (logicKey == LogicalKeyboardKey.altRight) {
  184. lastRAltKeyEvent = e;
  185. }
  186. } else if (e.isControlPressed) {
  187. if (logicKey == LogicalKeyboardKey.controlLeft) {
  188. lastLCtrlKeyEvent = e;
  189. } else if (logicKey == LogicalKeyboardKey.controlRight) {
  190. lastRCtrlKeyEvent = e;
  191. }
  192. } else if (e.isShiftPressed) {
  193. if (logicKey == LogicalKeyboardKey.shiftLeft) {
  194. lastLShiftKeyEvent = e;
  195. } else if (logicKey == LogicalKeyboardKey.shiftRight) {
  196. lastRShiftKeyEvent = e;
  197. }
  198. } else if (e.isMetaPressed) {
  199. if (logicKey == LogicalKeyboardKey.metaLeft) {
  200. lastLCommandKeyEvent = e;
  201. } else if (logicKey == LogicalKeyboardKey.metaRight) {
  202. lastRCommandKeyEvent = e;
  203. } else if (logicKey == LogicalKeyboardKey.superKey) {
  204. lastSuperKeyEvent = e;
  205. }
  206. }
  207. }
  208. updateKeyUp(LogicalKeyboardKey logicKey, RawKeyUpEvent e) {
  209. if (e.isAltPressed) {
  210. if (logicKey == LogicalKeyboardKey.altLeft) {
  211. lastLAltKeyEvent = null;
  212. } else if (logicKey == LogicalKeyboardKey.altRight) {
  213. lastRAltKeyEvent = null;
  214. }
  215. } else if (e.isControlPressed) {
  216. if (logicKey == LogicalKeyboardKey.controlLeft) {
  217. lastLCtrlKeyEvent = null;
  218. } else if (logicKey == LogicalKeyboardKey.controlRight) {
  219. lastRCtrlKeyEvent = null;
  220. }
  221. } else if (e.isShiftPressed) {
  222. if (logicKey == LogicalKeyboardKey.shiftLeft) {
  223. lastLShiftKeyEvent = null;
  224. } else if (logicKey == LogicalKeyboardKey.shiftRight) {
  225. lastRShiftKeyEvent = null;
  226. }
  227. } else if (e.isMetaPressed) {
  228. if (logicKey == LogicalKeyboardKey.metaLeft) {
  229. lastLCommandKeyEvent = null;
  230. } else if (logicKey == LogicalKeyboardKey.metaRight) {
  231. lastRCommandKeyEvent = null;
  232. } else if (logicKey == LogicalKeyboardKey.superKey) {
  233. lastSuperKeyEvent = null;
  234. }
  235. }
  236. }
  237. release(KeyEventResult Function(RawKeyEvent e) handleRawKeyEvent) {
  238. for (final key in [
  239. lastLShiftKeyEvent,
  240. lastRShiftKeyEvent,
  241. lastLCtrlKeyEvent,
  242. lastRCtrlKeyEvent,
  243. lastLAltKeyEvent,
  244. lastRAltKeyEvent,
  245. lastLCommandKeyEvent,
  246. lastRCommandKeyEvent,
  247. lastSuperKeyEvent,
  248. ]) {
  249. if (key != null) {
  250. handleRawKeyEvent(RawKeyUpEvent(
  251. data: key.data,
  252. character: key.character,
  253. ));
  254. }
  255. }
  256. }
  257. }
  258. class ToReleaseKeys {
  259. KeyEvent? lastLShiftKeyEvent;
  260. KeyEvent? lastRShiftKeyEvent;
  261. KeyEvent? lastLCtrlKeyEvent;
  262. KeyEvent? lastRCtrlKeyEvent;
  263. KeyEvent? lastLAltKeyEvent;
  264. KeyEvent? lastRAltKeyEvent;
  265. KeyEvent? lastLCommandKeyEvent;
  266. KeyEvent? lastRCommandKeyEvent;
  267. KeyEvent? lastSuperKeyEvent;
  268. reset() {
  269. lastLShiftKeyEvent = null;
  270. lastRShiftKeyEvent = null;
  271. lastLCtrlKeyEvent = null;
  272. lastRCtrlKeyEvent = null;
  273. lastLAltKeyEvent = null;
  274. lastRAltKeyEvent = null;
  275. lastLCommandKeyEvent = null;
  276. lastRCommandKeyEvent = null;
  277. lastSuperKeyEvent = null;
  278. }
  279. release(KeyEventResult Function(KeyEvent e) handleKeyEvent) {
  280. for (final key in [
  281. lastLShiftKeyEvent,
  282. lastRShiftKeyEvent,
  283. lastLCtrlKeyEvent,
  284. lastRCtrlKeyEvent,
  285. lastLAltKeyEvent,
  286. lastRAltKeyEvent,
  287. lastLCommandKeyEvent,
  288. lastRCommandKeyEvent,
  289. lastSuperKeyEvent,
  290. ]) {
  291. if (key != null) {
  292. handleKeyEvent(key);
  293. }
  294. }
  295. }
  296. }
  297. class InputModel {
  298. final WeakReference<FFI> parent;
  299. String keyboardMode = '';
  300. // keyboard
  301. var shift = false;
  302. var ctrl = false;
  303. var alt = false;
  304. var command = false;
  305. final ToReleaseRawKeys toReleaseRawKeys = ToReleaseRawKeys();
  306. final ToReleaseKeys toReleaseKeys = ToReleaseKeys();
  307. // trackpad
  308. var _trackpadLastDelta = Offset.zero;
  309. var _stopFling = true;
  310. var _fling = false;
  311. Timer? _flingTimer;
  312. final _flingBaseDelay = 30;
  313. final _trackpadAdjustPeerLinux = 0.06;
  314. // This is an experience value.
  315. final _trackpadAdjustMacToWin = 2.50;
  316. int _trackpadSpeed = kDefaultTrackpadSpeed;
  317. double _trackpadSpeedInner = kDefaultTrackpadSpeed / 100.0;
  318. var _trackpadScrollUnsent = Offset.zero;
  319. var _lastScale = 1.0;
  320. bool _pointerMovedAfterEnter = false;
  321. // mouse
  322. final isPhysicalMouse = false.obs;
  323. int _lastButtons = 0;
  324. Offset lastMousePos = Offset.zero;
  325. bool _queryOtherWindowCoords = false;
  326. Rect? _windowRect;
  327. List<RemoteWindowCoords> _remoteWindowCoords = [];
  328. late final SessionID sessionId;
  329. bool get keyboardPerm => parent.target!.ffiModel.keyboard;
  330. String get id => parent.target?.id ?? '';
  331. String? get peerPlatform => parent.target?.ffiModel.pi.platform;
  332. bool get isViewOnly => parent.target!.ffiModel.viewOnly;
  333. double get devicePixelRatio => parent.target!.canvasModel.devicePixelRatio;
  334. bool get isViewCamera => parent.target!.connType == ConnType.viewCamera;
  335. int get trackpadSpeed => _trackpadSpeed;
  336. InputModel(this.parent) {
  337. sessionId = parent.target!.sessionId;
  338. }
  339. // This function must be called after the peer info is received.
  340. // Because `sessionGetKeyboardMode` relies on the peer version.
  341. updateKeyboardMode() async {
  342. // * Currently mobile does not enable map mode
  343. if (isDesktop || isWebDesktop) {
  344. keyboardMode = await bind.sessionGetKeyboardMode(sessionId: sessionId) ??
  345. kKeyLegacyMode;
  346. }
  347. }
  348. /// Updates the trackpad speed based on the session value.
  349. ///
  350. /// The expected format of the retrieved value is a string that can be parsed into a double.
  351. /// If parsing fails or the value is out of bounds (less than `kMinTrackpadSpeed` or greater
  352. /// than `kMaxTrackpadSpeed`), the trackpad speed is reset to the default
  353. /// value (`kDefaultTrackpadSpeed`).
  354. ///
  355. /// Bounds:
  356. /// - Minimum: `kMinTrackpadSpeed`
  357. /// - Maximum: `kMaxTrackpadSpeed`
  358. /// - Default: `kDefaultTrackpadSpeed`
  359. Future<void> updateTrackpadSpeed() async {
  360. _trackpadSpeed =
  361. (await bind.sessionGetTrackpadSpeed(sessionId: sessionId) ??
  362. kDefaultTrackpadSpeed);
  363. if (_trackpadSpeed < kMinTrackpadSpeed ||
  364. _trackpadSpeed > kMaxTrackpadSpeed) {
  365. _trackpadSpeed = kDefaultTrackpadSpeed;
  366. }
  367. _trackpadSpeedInner = _trackpadSpeed / 100.0;
  368. }
  369. void handleKeyDownEventModifiers(KeyEvent e) {
  370. KeyUpEvent upEvent(e) => KeyUpEvent(
  371. physicalKey: e.physicalKey,
  372. logicalKey: e.logicalKey,
  373. timeStamp: e.timeStamp,
  374. );
  375. if (e.logicalKey == LogicalKeyboardKey.altLeft) {
  376. if (!alt) {
  377. alt = true;
  378. }
  379. toReleaseKeys.lastLAltKeyEvent = upEvent(e);
  380. } else if (e.logicalKey == LogicalKeyboardKey.altRight) {
  381. if (!alt) {
  382. alt = true;
  383. }
  384. toReleaseKeys.lastLAltKeyEvent = upEvent(e);
  385. } else if (e.logicalKey == LogicalKeyboardKey.controlLeft) {
  386. if (!ctrl) {
  387. ctrl = true;
  388. }
  389. toReleaseKeys.lastLCtrlKeyEvent = upEvent(e);
  390. } else if (e.logicalKey == LogicalKeyboardKey.controlRight) {
  391. if (!ctrl) {
  392. ctrl = true;
  393. }
  394. toReleaseKeys.lastRCtrlKeyEvent = upEvent(e);
  395. } else if (e.logicalKey == LogicalKeyboardKey.shiftLeft) {
  396. if (!shift) {
  397. shift = true;
  398. }
  399. toReleaseKeys.lastLShiftKeyEvent = upEvent(e);
  400. } else if (e.logicalKey == LogicalKeyboardKey.shiftRight) {
  401. if (!shift) {
  402. shift = true;
  403. }
  404. toReleaseKeys.lastRShiftKeyEvent = upEvent(e);
  405. } else if (e.logicalKey == LogicalKeyboardKey.metaLeft) {
  406. if (!command) {
  407. command = true;
  408. }
  409. toReleaseKeys.lastLCommandKeyEvent = upEvent(e);
  410. } else if (e.logicalKey == LogicalKeyboardKey.metaRight) {
  411. if (!command) {
  412. command = true;
  413. }
  414. toReleaseKeys.lastRCommandKeyEvent = upEvent(e);
  415. } else if (e.logicalKey == LogicalKeyboardKey.superKey) {
  416. if (!command) {
  417. command = true;
  418. }
  419. toReleaseKeys.lastSuperKeyEvent = upEvent(e);
  420. }
  421. }
  422. void handleKeyUpEventModifiers(KeyEvent e) {
  423. if (e.logicalKey == LogicalKeyboardKey.altLeft) {
  424. alt = false;
  425. toReleaseKeys.lastLAltKeyEvent = null;
  426. } else if (e.logicalKey == LogicalKeyboardKey.altRight) {
  427. alt = false;
  428. toReleaseKeys.lastRAltKeyEvent = null;
  429. } else if (e.logicalKey == LogicalKeyboardKey.controlLeft) {
  430. ctrl = false;
  431. toReleaseKeys.lastLCtrlKeyEvent = null;
  432. } else if (e.logicalKey == LogicalKeyboardKey.controlRight) {
  433. ctrl = false;
  434. toReleaseKeys.lastRCtrlKeyEvent = null;
  435. } else if (e.logicalKey == LogicalKeyboardKey.shiftLeft) {
  436. shift = false;
  437. toReleaseKeys.lastLShiftKeyEvent = null;
  438. } else if (e.logicalKey == LogicalKeyboardKey.shiftRight) {
  439. shift = false;
  440. toReleaseKeys.lastRShiftKeyEvent = null;
  441. } else if (e.logicalKey == LogicalKeyboardKey.metaLeft) {
  442. command = false;
  443. toReleaseKeys.lastLCommandKeyEvent = null;
  444. } else if (e.logicalKey == LogicalKeyboardKey.metaRight) {
  445. command = false;
  446. toReleaseKeys.lastRCommandKeyEvent = null;
  447. } else if (e.logicalKey == LogicalKeyboardKey.superKey) {
  448. command = false;
  449. toReleaseKeys.lastSuperKeyEvent = null;
  450. }
  451. }
  452. KeyEventResult handleRawKeyEvent(RawKeyEvent e) {
  453. if (isViewOnly) return KeyEventResult.handled;
  454. if (isViewCamera) return KeyEventResult.handled;
  455. if (!isInputSourceFlutter) {
  456. if (isDesktop) {
  457. return KeyEventResult.handled;
  458. } else if (isWeb) {
  459. return KeyEventResult.ignored;
  460. }
  461. }
  462. final key = e.logicalKey;
  463. if (e is RawKeyDownEvent) {
  464. if (!e.repeat) {
  465. if (e.isAltPressed && !alt) {
  466. alt = true;
  467. } else if (e.isControlPressed && !ctrl) {
  468. ctrl = true;
  469. } else if (e.isShiftPressed && !shift) {
  470. shift = true;
  471. } else if (e.isMetaPressed && !command) {
  472. command = true;
  473. }
  474. }
  475. toReleaseRawKeys.updateKeyDown(key, e);
  476. }
  477. if (e is RawKeyUpEvent) {
  478. if (key == LogicalKeyboardKey.altLeft ||
  479. key == LogicalKeyboardKey.altRight) {
  480. alt = false;
  481. } else if (key == LogicalKeyboardKey.controlLeft ||
  482. key == LogicalKeyboardKey.controlRight) {
  483. ctrl = false;
  484. } else if (key == LogicalKeyboardKey.shiftRight ||
  485. key == LogicalKeyboardKey.shiftLeft) {
  486. shift = false;
  487. } else if (key == LogicalKeyboardKey.metaLeft ||
  488. key == LogicalKeyboardKey.metaRight ||
  489. key == LogicalKeyboardKey.superKey) {
  490. command = false;
  491. }
  492. toReleaseRawKeys.updateKeyUp(key, e);
  493. }
  494. // * Currently mobile does not enable map mode
  495. if ((isDesktop || isWebDesktop) && keyboardMode == kKeyMapMode) {
  496. mapKeyboardModeRaw(e);
  497. } else {
  498. legacyKeyboardModeRaw(e);
  499. }
  500. return KeyEventResult.handled;
  501. }
  502. KeyEventResult handleKeyEvent(KeyEvent e) {
  503. if (isViewOnly) return KeyEventResult.handled;
  504. if (isViewCamera) return KeyEventResult.handled;
  505. if (!isInputSourceFlutter) {
  506. if (isDesktop) {
  507. return KeyEventResult.handled;
  508. } else if (isWeb) {
  509. return KeyEventResult.ignored;
  510. }
  511. }
  512. if (isWindows || isLinux) {
  513. // Ignore meta keys. Because flutter window will loose focus if meta key is pressed.
  514. if (e.physicalKey == PhysicalKeyboardKey.metaLeft ||
  515. e.physicalKey == PhysicalKeyboardKey.metaRight) {
  516. return KeyEventResult.handled;
  517. }
  518. }
  519. if (e is KeyUpEvent) {
  520. handleKeyUpEventModifiers(e);
  521. } else if (e is KeyDownEvent) {
  522. handleKeyDownEventModifiers(e);
  523. }
  524. bool isMobileAndMapMode = false;
  525. if (isMobile) {
  526. // Do not use map mode if mobile -> Android. Android does not support map mode for now.
  527. // Because simulating the physical key events(uhid) which requires root permission is not supported.
  528. if (peerPlatform != kPeerPlatformAndroid) {
  529. if (isIOS) {
  530. isMobileAndMapMode = true;
  531. } else {
  532. // The physicalKey.usbHidUsage may be not correct for soft keyboard on Android.
  533. // iOS does not have this issue.
  534. // 1. Open the soft keyboard on Android
  535. // 2. Switch to input method like zh/ko/ja
  536. // 3. Click Backspace and Enter on the soft keyboard or physical keyboard
  537. // 4. The physicalKey.usbHidUsage is not correct.
  538. // PhysicalKeyboardKey#8ac83(usbHidUsage: "0x1100000042", debugName: "Key with ID 0x1100000042")
  539. // LogicalKeyboardKey#2604c(keyId: "0x10000000d", keyLabel: "Enter", debugName: "Enter")
  540. //
  541. // The correct PhysicalKeyboardKey should be
  542. // PhysicalKeyboardKey#e14a9(usbHidUsage: "0x00070028", debugName: "Enter")
  543. // https://github.com/flutter/flutter/issues/157771
  544. // We cannot use the debugName to determine the key is correct or not, because it's null in release mode.
  545. // The normal `usbHidUsage` for keyboard shoud be between [0x00000010, 0x000c029f]
  546. // https://github.com/flutter/flutter/blob/c051b69e2a2224300e20d93dbd15f4b91e8844d1/packages/flutter/lib/src/services/keyboard_key.g.dart#L5332 - 5600
  547. final isNormalHsbHidUsage = (e.physicalKey.usbHidUsage >> 20) == 0;
  548. isMobileAndMapMode = isNormalHsbHidUsage &&
  549. // No need to check `!['Backspace', 'Enter'].contains(e.logicalKey.keyLabel)`
  550. // But we still add it for more reliability.
  551. !['Backspace', 'Enter'].contains(e.logicalKey.keyLabel);
  552. }
  553. }
  554. }
  555. final isDesktopAndMapMode =
  556. isDesktop || (isWebDesktop && keyboardMode == kKeyMapMode);
  557. if (isMobileAndMapMode || isDesktopAndMapMode) {
  558. // FIXME: e.character is wrong for dead keys, eg: ^ in de
  559. newKeyboardMode(
  560. e.character ?? '',
  561. e.physicalKey.usbHidUsage & 0xFFFF,
  562. // Show repeat event be converted to "release+press" events?
  563. e is KeyDownEvent || e is KeyRepeatEvent);
  564. } else {
  565. legacyKeyboardMode(e);
  566. }
  567. return KeyEventResult.handled;
  568. }
  569. /// Send Key Event
  570. void newKeyboardMode(String character, int usbHid, bool down) {
  571. const capslock = 1;
  572. const numlock = 2;
  573. const scrolllock = 3;
  574. int lockModes = 0;
  575. if (HardwareKeyboard.instance.lockModesEnabled
  576. .contains(KeyboardLockMode.capsLock)) {
  577. lockModes |= (1 << capslock);
  578. }
  579. if (HardwareKeyboard.instance.lockModesEnabled
  580. .contains(KeyboardLockMode.numLock)) {
  581. lockModes |= (1 << numlock);
  582. }
  583. if (HardwareKeyboard.instance.lockModesEnabled
  584. .contains(KeyboardLockMode.scrollLock)) {
  585. lockModes |= (1 << scrolllock);
  586. }
  587. bind.sessionHandleFlutterKeyEvent(
  588. sessionId: sessionId,
  589. character: character,
  590. usbHid: usbHid,
  591. lockModes: lockModes,
  592. downOrUp: down);
  593. }
  594. void mapKeyboardModeRaw(RawKeyEvent e) {
  595. int positionCode = -1;
  596. int platformCode = -1;
  597. bool down;
  598. if (e.data is RawKeyEventDataMacOs) {
  599. RawKeyEventDataMacOs newData = e.data as RawKeyEventDataMacOs;
  600. positionCode = newData.keyCode;
  601. platformCode = newData.keyCode;
  602. } else if (e.data is RawKeyEventDataWindows) {
  603. RawKeyEventDataWindows newData = e.data as RawKeyEventDataWindows;
  604. positionCode = newData.scanCode;
  605. platformCode = newData.keyCode;
  606. } else if (e.data is RawKeyEventDataLinux) {
  607. RawKeyEventDataLinux newData = e.data as RawKeyEventDataLinux;
  608. // scanCode and keyCode of RawKeyEventDataLinux are incorrect.
  609. // 1. scanCode means keycode
  610. // 2. keyCode means keysym
  611. positionCode = newData.scanCode;
  612. platformCode = newData.keyCode;
  613. } else if (e.data is RawKeyEventDataAndroid) {
  614. RawKeyEventDataAndroid newData = e.data as RawKeyEventDataAndroid;
  615. positionCode = newData.scanCode + 8;
  616. platformCode = newData.keyCode;
  617. } else {}
  618. if (e is RawKeyDownEvent) {
  619. down = true;
  620. } else {
  621. down = false;
  622. }
  623. inputRawKey(e.character ?? '', platformCode, positionCode, down);
  624. }
  625. /// Send raw Key Event
  626. void inputRawKey(String name, int platformCode, int positionCode, bool down) {
  627. const capslock = 1;
  628. const numlock = 2;
  629. const scrolllock = 3;
  630. int lockModes = 0;
  631. if (HardwareKeyboard.instance.lockModesEnabled
  632. .contains(KeyboardLockMode.capsLock)) {
  633. lockModes |= (1 << capslock);
  634. }
  635. if (HardwareKeyboard.instance.lockModesEnabled
  636. .contains(KeyboardLockMode.numLock)) {
  637. lockModes |= (1 << numlock);
  638. }
  639. if (HardwareKeyboard.instance.lockModesEnabled
  640. .contains(KeyboardLockMode.scrollLock)) {
  641. lockModes |= (1 << scrolllock);
  642. }
  643. bind.sessionHandleFlutterRawKeyEvent(
  644. sessionId: sessionId,
  645. name: name,
  646. platformCode: platformCode,
  647. positionCode: positionCode,
  648. lockModes: lockModes,
  649. downOrUp: down);
  650. }
  651. void legacyKeyboardModeRaw(RawKeyEvent e) {
  652. if (e is RawKeyDownEvent) {
  653. if (e.repeat) {
  654. sendRawKey(e, press: true);
  655. } else {
  656. sendRawKey(e, down: true);
  657. }
  658. }
  659. if (e is RawKeyUpEvent) {
  660. sendRawKey(e);
  661. }
  662. }
  663. void sendRawKey(RawKeyEvent e, {bool? down, bool? press}) {
  664. // for maximum compatibility
  665. final label = physicalKeyMap[e.physicalKey.usbHidUsage] ??
  666. logicalKeyMap[e.logicalKey.keyId] ??
  667. e.logicalKey.keyLabel;
  668. inputKey(label, down: down, press: press ?? false);
  669. }
  670. void legacyKeyboardMode(KeyEvent e) {
  671. if (e is KeyDownEvent) {
  672. sendKey(e, down: true);
  673. } else if (e is KeyRepeatEvent) {
  674. sendKey(e, press: true);
  675. } else if (e is KeyUpEvent) {
  676. sendKey(e);
  677. }
  678. }
  679. void sendKey(KeyEvent e, {bool? down, bool? press}) {
  680. // for maximum compatibility
  681. final label = physicalKeyMap[e.physicalKey.usbHidUsage] ??
  682. logicalKeyMap[e.logicalKey.keyId] ??
  683. e.logicalKey.keyLabel;
  684. inputKey(label, down: down, press: press ?? false);
  685. }
  686. /// Send key stroke event.
  687. /// [down] indicates the key's state(down or up).
  688. /// [press] indicates a click event(down and up).
  689. void inputKey(String name, {bool? down, bool? press}) {
  690. if (!keyboardPerm) return;
  691. if (isViewCamera) return;
  692. bind.sessionInputKey(
  693. sessionId: sessionId,
  694. name: name,
  695. down: down ?? false,
  696. press: press ?? true,
  697. alt: alt,
  698. ctrl: ctrl,
  699. shift: shift,
  700. command: command);
  701. }
  702. Map<String, dynamic> _getMouseEvent(PointerEvent evt, String type) {
  703. final Map<String, dynamic> out = {};
  704. // Check update event type and set buttons to be sent.
  705. int buttons = _lastButtons;
  706. if (type == _kMouseEventMove) {
  707. // flutter may emit move event if one button is pressed and another button
  708. // is pressing or releasing.
  709. if (evt.buttons != _lastButtons) {
  710. // For simplicity
  711. // Just consider 3 - 1 ((Left + Right buttons) - Left button)
  712. // Do not consider 2 - 1 (Right button - Left button)
  713. // or 6 - 5 ((Right + Mid buttons) - (Left + Mid buttons))
  714. // and so on
  715. buttons = evt.buttons - _lastButtons;
  716. if (buttons > 0) {
  717. type = _kMouseEventDown;
  718. } else {
  719. type = _kMouseEventUp;
  720. buttons = -buttons;
  721. }
  722. }
  723. } else {
  724. if (evt.buttons != 0) {
  725. buttons = evt.buttons;
  726. }
  727. }
  728. _lastButtons = evt.buttons;
  729. out['buttons'] = buttons;
  730. out['type'] = type;
  731. return out;
  732. }
  733. /// Send a mouse tap event(down and up).
  734. Future<void> tap(MouseButtons button) async {
  735. await sendMouse('down', button);
  736. await sendMouse('up', button);
  737. }
  738. Future<void> tapDown(MouseButtons button) async {
  739. await sendMouse('down', button);
  740. }
  741. Future<void> tapUp(MouseButtons button) async {
  742. await sendMouse('up', button);
  743. }
  744. /// Send scroll event with scroll distance [y].
  745. Future<void> scroll(int y) async {
  746. if (isViewCamera) return;
  747. await bind.sessionSendMouse(
  748. sessionId: sessionId,
  749. msg: json
  750. .encode(modify({'id': id, 'type': 'wheel', 'y': y.toString()})));
  751. }
  752. /// Reset key modifiers to false, including [shift], [ctrl], [alt] and [command].
  753. void resetModifiers() {
  754. shift = ctrl = alt = command = false;
  755. }
  756. /// Modify the given modifier map [evt] based on current modifier key status.
  757. Map<String, dynamic> modify(Map<String, dynamic> evt) {
  758. if (ctrl) evt['ctrl'] = 'true';
  759. if (shift) evt['shift'] = 'true';
  760. if (alt) evt['alt'] = 'true';
  761. if (command) evt['command'] = 'true';
  762. return evt;
  763. }
  764. /// Send mouse press event.
  765. Future<void> sendMouse(String type, MouseButtons button) async {
  766. if (!keyboardPerm) return;
  767. if (isViewCamera) return;
  768. await bind.sessionSendMouse(
  769. sessionId: sessionId,
  770. msg: json.encode(modify({'type': type, 'buttons': button.value})));
  771. }
  772. void enterOrLeave(bool enter) {
  773. toReleaseKeys.release(handleKeyEvent);
  774. toReleaseRawKeys.release(handleRawKeyEvent);
  775. _pointerMovedAfterEnter = false;
  776. // Fix status
  777. if (!enter) {
  778. resetModifiers();
  779. }
  780. _flingTimer?.cancel();
  781. if (!isInputSourceFlutter) {
  782. bind.sessionEnterOrLeave(sessionId: sessionId, enter: enter);
  783. }
  784. if (!isWeb && enter) {
  785. bind.setCurSessionId(sessionId: sessionId);
  786. }
  787. }
  788. /// Send mouse movement event with distance in [x] and [y].
  789. Future<void> moveMouse(double x, double y) async {
  790. if (!keyboardPerm) return;
  791. if (isViewCamera) return;
  792. var x2 = x.toInt();
  793. var y2 = y.toInt();
  794. await bind.sessionSendMouse(
  795. sessionId: sessionId,
  796. msg: json.encode(modify({'x': '$x2', 'y': '$y2'})));
  797. }
  798. void onPointHoverImage(PointerHoverEvent e) {
  799. _stopFling = true;
  800. if (isViewOnly) return;
  801. if (e.kind != ui.PointerDeviceKind.mouse) return;
  802. if (!isPhysicalMouse.value) {
  803. isPhysicalMouse.value = true;
  804. }
  805. if (isPhysicalMouse.value) {
  806. handleMouse(_getMouseEvent(e, _kMouseEventMove), e.position);
  807. }
  808. }
  809. void onPointerPanZoomStart(PointerPanZoomStartEvent e) {
  810. _lastScale = 1.0;
  811. _stopFling = true;
  812. if (isViewOnly) return;
  813. if (isViewCamera) return;
  814. if (peerPlatform == kPeerPlatformAndroid) {
  815. handlePointerEvent('touch', kMouseEventTypePanStart, e.position);
  816. }
  817. }
  818. // https://docs.flutter.dev/release/breaking-changes/trackpad-gestures
  819. void onPointerPanZoomUpdate(PointerPanZoomUpdateEvent e) {
  820. if (isViewOnly) return;
  821. if (isViewCamera) return;
  822. if (peerPlatform != kPeerPlatformAndroid) {
  823. final scale = ((e.scale - _lastScale) * 1000).toInt();
  824. _lastScale = e.scale;
  825. if (scale != 0) {
  826. bind.sessionSendPointer(
  827. sessionId: sessionId,
  828. msg: json.encode(
  829. PointerEventToRust(kPointerEventKindTouch, 'scale', scale)
  830. .toJson()));
  831. return;
  832. }
  833. }
  834. var delta = e.panDelta * _trackpadSpeedInner;
  835. if (isMacOS && peerPlatform == kPeerPlatformWindows) {
  836. delta *= _trackpadAdjustMacToWin;
  837. }
  838. _trackpadLastDelta = delta;
  839. var x = delta.dx.toInt();
  840. var y = delta.dy.toInt();
  841. if (peerPlatform == kPeerPlatformLinux) {
  842. _trackpadScrollUnsent += (delta * _trackpadAdjustPeerLinux);
  843. x = _trackpadScrollUnsent.dx.truncate();
  844. y = _trackpadScrollUnsent.dy.truncate();
  845. _trackpadScrollUnsent -= Offset(x.toDouble(), y.toDouble());
  846. } else {
  847. if (x == 0 && y == 0) {
  848. final thr = 0.1;
  849. if (delta.dx.abs() > delta.dy.abs()) {
  850. x = delta.dx > thr ? 1 : (delta.dx < -thr ? -1 : 0);
  851. } else {
  852. y = delta.dy > thr ? 1 : (delta.dy < -thr ? -1 : 0);
  853. }
  854. }
  855. }
  856. if (x != 0 || y != 0) {
  857. if (peerPlatform == kPeerPlatformAndroid) {
  858. handlePointerEvent('touch', kMouseEventTypePanUpdate,
  859. Offset(x.toDouble(), y.toDouble()));
  860. } else {
  861. if (isViewCamera) return;
  862. bind.sessionSendMouse(
  863. sessionId: sessionId,
  864. msg: '{"type": "trackpad", "x": "$x", "y": "$y"}');
  865. }
  866. }
  867. }
  868. void _scheduleFling(double x, double y, int delay) {
  869. if (isViewCamera) return;
  870. if ((x == 0 && y == 0) || _stopFling) {
  871. _fling = false;
  872. return;
  873. }
  874. _flingTimer = Timer(Duration(milliseconds: delay), () {
  875. if (_stopFling) {
  876. _fling = false;
  877. return;
  878. }
  879. final d = 0.97;
  880. x *= d;
  881. y *= d;
  882. // Try set delta (x,y) and delay.
  883. var dx = x.toInt();
  884. var dy = y.toInt();
  885. if (parent.target?.ffiModel.pi.platform == kPeerPlatformLinux) {
  886. dx = (x * _trackpadAdjustPeerLinux).toInt();
  887. dy = (y * _trackpadAdjustPeerLinux).toInt();
  888. }
  889. var delay = _flingBaseDelay;
  890. if (dx == 0 && dy == 0) {
  891. _fling = false;
  892. return;
  893. }
  894. bind.sessionSendMouse(
  895. sessionId: sessionId,
  896. msg: '{"type": "trackpad", "x": "$dx", "y": "$dy"}');
  897. _scheduleFling(x, y, delay);
  898. });
  899. }
  900. void waitLastFlingDone() {
  901. if (_fling) {
  902. _stopFling = true;
  903. }
  904. for (var i = 0; i < 5; i++) {
  905. if (!_fling) {
  906. break;
  907. }
  908. sleep(Duration(milliseconds: 10));
  909. }
  910. _flingTimer?.cancel();
  911. }
  912. void onPointerPanZoomEnd(PointerPanZoomEndEvent e) {
  913. if (isViewCamera) return;
  914. if (peerPlatform == kPeerPlatformAndroid) {
  915. handlePointerEvent('touch', kMouseEventTypePanEnd, e.position);
  916. return;
  917. }
  918. bind.sessionSendPointer(
  919. sessionId: sessionId,
  920. msg: json.encode(
  921. PointerEventToRust(kPointerEventKindTouch, 'scale', 0).toJson()));
  922. waitLastFlingDone();
  923. _stopFling = false;
  924. // 2.0 is an experience value
  925. double minFlingValue = 2.0 * _trackpadSpeedInner;
  926. if (isMacOS && peerPlatform == kPeerPlatformWindows) {
  927. minFlingValue *= _trackpadAdjustMacToWin;
  928. }
  929. if (_trackpadLastDelta.dx.abs() > minFlingValue ||
  930. _trackpadLastDelta.dy.abs() > minFlingValue) {
  931. _fling = true;
  932. _scheduleFling(
  933. _trackpadLastDelta.dx, _trackpadLastDelta.dy, _flingBaseDelay);
  934. }
  935. _trackpadLastDelta = Offset.zero;
  936. }
  937. void onPointDownImage(PointerDownEvent e) {
  938. debugPrint("onPointDownImage ${e.kind}");
  939. _stopFling = true;
  940. if (isDesktop) _queryOtherWindowCoords = true;
  941. _remoteWindowCoords = [];
  942. _windowRect = null;
  943. if (isViewOnly) return;
  944. if (isViewCamera) return;
  945. if (e.kind != ui.PointerDeviceKind.mouse) {
  946. if (isPhysicalMouse.value) {
  947. isPhysicalMouse.value = false;
  948. }
  949. }
  950. if (isPhysicalMouse.value) {
  951. handleMouse(_getMouseEvent(e, _kMouseEventDown), e.position);
  952. }
  953. }
  954. void onPointUpImage(PointerUpEvent e) {
  955. if (isDesktop) _queryOtherWindowCoords = false;
  956. if (isViewOnly) return;
  957. if (isViewCamera) return;
  958. if (e.kind != ui.PointerDeviceKind.mouse) return;
  959. if (isPhysicalMouse.value) {
  960. handleMouse(_getMouseEvent(e, _kMouseEventUp), e.position);
  961. }
  962. }
  963. void onPointMoveImage(PointerMoveEvent e) {
  964. if (isViewOnly) return;
  965. if (isViewCamera) return;
  966. if (e.kind != ui.PointerDeviceKind.mouse) return;
  967. if (_queryOtherWindowCoords) {
  968. Future.delayed(Duration.zero, () async {
  969. _windowRect = await fillRemoteCoordsAndGetCurFrame(_remoteWindowCoords);
  970. });
  971. _queryOtherWindowCoords = false;
  972. }
  973. if (isPhysicalMouse.value) {
  974. handleMouse(_getMouseEvent(e, _kMouseEventMove), e.position);
  975. }
  976. }
  977. static Future<Rect?> fillRemoteCoordsAndGetCurFrame(
  978. List<RemoteWindowCoords> remoteWindowCoords) async {
  979. final coords =
  980. await rustDeskWinManager.getOtherRemoteWindowCoordsFromMain();
  981. final wc = WindowController.fromWindowId(kWindowId!);
  982. try {
  983. final frame = await wc.getFrame();
  984. for (final c in coords) {
  985. c.relativeOffset = Offset(
  986. c.windowRect.left - frame.left, c.windowRect.top - frame.top);
  987. remoteWindowCoords.add(c);
  988. }
  989. return frame;
  990. } catch (e) {
  991. // Unreachable code
  992. debugPrint("Failed to get frame of window $kWindowId, it may be hidden");
  993. }
  994. return null;
  995. }
  996. void onPointerSignalImage(PointerSignalEvent e) {
  997. if (isViewOnly) return;
  998. if (isViewCamera) return;
  999. if (e is PointerScrollEvent) {
  1000. var dx = e.scrollDelta.dx.toInt();
  1001. var dy = e.scrollDelta.dy.toInt();
  1002. if (dx > 0) {
  1003. dx = -1;
  1004. } else if (dx < 0) {
  1005. dx = 1;
  1006. }
  1007. if (dy > 0) {
  1008. dy = -1;
  1009. } else if (dy < 0) {
  1010. dy = 1;
  1011. }
  1012. bind.sessionSendMouse(
  1013. sessionId: sessionId,
  1014. msg: '{"type": "wheel", "x": "$dx", "y": "$dy"}');
  1015. }
  1016. }
  1017. void refreshMousePos() => handleMouse({
  1018. 'buttons': 0,
  1019. 'type': _kMouseEventMove,
  1020. }, lastMousePos);
  1021. void tryMoveEdgeOnExit(Offset pos) => handleMouse(
  1022. {
  1023. 'buttons': 0,
  1024. 'type': _kMouseEventMove,
  1025. },
  1026. pos,
  1027. onExit: true,
  1028. );
  1029. static double tryGetNearestRange(double v, double min, double max, double n) {
  1030. if (v < min && v >= min - n) {
  1031. v = min;
  1032. }
  1033. if (v > max && v <= max + n) {
  1034. v = max;
  1035. }
  1036. return v;
  1037. }
  1038. Offset setNearestEdge(double x, double y, Rect rect) {
  1039. double left = x - rect.left;
  1040. double right = rect.right - 1 - x;
  1041. double top = y - rect.top;
  1042. double bottom = rect.bottom - 1 - y;
  1043. if (left < right && left < top && left < bottom) {
  1044. x = rect.left;
  1045. }
  1046. if (right < left && right < top && right < bottom) {
  1047. x = rect.right - 1;
  1048. }
  1049. if (top < left && top < right && top < bottom) {
  1050. y = rect.top;
  1051. }
  1052. if (bottom < left && bottom < right && bottom < top) {
  1053. y = rect.bottom - 1;
  1054. }
  1055. return Offset(x, y);
  1056. }
  1057. void handlePointerEvent(String kind, String type, Offset offset) {
  1058. double x = offset.dx;
  1059. double y = offset.dy;
  1060. if (_checkPeerControlProtected(x, y)) {
  1061. return;
  1062. }
  1063. // Only touch events are handled for now. So we can just ignore buttons.
  1064. // to-do: handle mouse events
  1065. late final dynamic evtValue;
  1066. if (type == kMouseEventTypePanUpdate) {
  1067. evtValue = {
  1068. 'x': x.toInt(),
  1069. 'y': y.toInt(),
  1070. };
  1071. } else {
  1072. final isMoveTypes = [kMouseEventTypePanStart, kMouseEventTypePanEnd];
  1073. final pos = handlePointerDevicePos(
  1074. kPointerEventKindTouch,
  1075. x,
  1076. y,
  1077. isMoveTypes.contains(type),
  1078. type,
  1079. );
  1080. if (pos == null) {
  1081. return;
  1082. }
  1083. evtValue = {
  1084. 'x': pos.x.toInt(),
  1085. 'y': pos.y.toInt(),
  1086. };
  1087. }
  1088. final evt = PointerEventToRust(kind, type, evtValue).toJson();
  1089. if (isViewCamera) return;
  1090. bind.sessionSendPointer(
  1091. sessionId: sessionId, msg: json.encode(modify(evt)));
  1092. }
  1093. bool _checkPeerControlProtected(double x, double y) {
  1094. final cursorModel = parent.target!.cursorModel;
  1095. if (cursorModel.isPeerControlProtected) {
  1096. lastMousePos = ui.Offset(x, y);
  1097. return true;
  1098. }
  1099. if (!cursorModel.gotMouseControl) {
  1100. bool selfGetControl =
  1101. (x - lastMousePos.dx).abs() > kMouseControlDistance ||
  1102. (y - lastMousePos.dy).abs() > kMouseControlDistance;
  1103. if (selfGetControl) {
  1104. cursorModel.gotMouseControl = true;
  1105. } else {
  1106. lastMousePos = ui.Offset(x, y);
  1107. return true;
  1108. }
  1109. }
  1110. lastMousePos = ui.Offset(x, y);
  1111. return false;
  1112. }
  1113. void handleMouse(
  1114. Map<String, dynamic> evt,
  1115. Offset offset, {
  1116. bool onExit = false,
  1117. }) {
  1118. if (isViewCamera) return;
  1119. double x = offset.dx;
  1120. double y = max(0.0, offset.dy);
  1121. if (_checkPeerControlProtected(x, y)) {
  1122. return;
  1123. }
  1124. var type = kMouseEventTypeDefault;
  1125. var isMove = false;
  1126. switch (evt['type']) {
  1127. case _kMouseEventDown:
  1128. type = kMouseEventTypeDown;
  1129. break;
  1130. case _kMouseEventUp:
  1131. type = kMouseEventTypeUp;
  1132. break;
  1133. case _kMouseEventMove:
  1134. _pointerMovedAfterEnter = true;
  1135. isMove = true;
  1136. break;
  1137. default:
  1138. return;
  1139. }
  1140. evt['type'] = type;
  1141. if (type == kMouseEventTypeDown && !_pointerMovedAfterEnter) {
  1142. // Move mouse to the position of the down event first.
  1143. lastMousePos = ui.Offset(x, y);
  1144. refreshMousePos();
  1145. }
  1146. final pos = handlePointerDevicePos(
  1147. kPointerEventKindMouse,
  1148. x,
  1149. y,
  1150. isMove,
  1151. type,
  1152. onExit: onExit,
  1153. buttons: evt['buttons'],
  1154. );
  1155. if (pos == null) {
  1156. return;
  1157. }
  1158. if (type != '') {
  1159. evt['x'] = '0';
  1160. evt['y'] = '0';
  1161. } else {
  1162. evt['x'] = '${pos.x.toInt()}';
  1163. evt['y'] = '${pos.y.toInt()}';
  1164. }
  1165. Map<int, String> mapButtons = {
  1166. kPrimaryMouseButton: 'left',
  1167. kSecondaryMouseButton: 'right',
  1168. kMiddleMouseButton: 'wheel',
  1169. kBackMouseButton: 'back',
  1170. kForwardMouseButton: 'forward'
  1171. };
  1172. evt['buttons'] = mapButtons[evt['buttons']] ?? '';
  1173. bind.sessionSendMouse(sessionId: sessionId, msg: json.encode(modify(evt)));
  1174. }
  1175. Point? handlePointerDevicePos(
  1176. String kind,
  1177. double x,
  1178. double y,
  1179. bool isMove,
  1180. String evtType, {
  1181. bool onExit = false,
  1182. int buttons = kPrimaryMouseButton,
  1183. }) {
  1184. final ffiModel = parent.target!.ffiModel;
  1185. CanvasCoords canvas =
  1186. CanvasCoords.fromCanvasModel(parent.target!.canvasModel);
  1187. Rect? rect = ffiModel.rect;
  1188. if (isMove) {
  1189. if (_remoteWindowCoords.isNotEmpty &&
  1190. _windowRect != null &&
  1191. !_isInCurrentWindow(x, y)) {
  1192. final coords =
  1193. findRemoteCoords(x, y, _remoteWindowCoords, devicePixelRatio);
  1194. if (coords != null) {
  1195. isMove = false;
  1196. canvas = coords.canvas;
  1197. rect = coords.remoteRect;
  1198. x -= coords.relativeOffset.dx / devicePixelRatio;
  1199. y -= coords.relativeOffset.dy / devicePixelRatio;
  1200. }
  1201. }
  1202. }
  1203. y -= CanvasModel.topToEdge;
  1204. x -= CanvasModel.leftToEdge;
  1205. if (isMove) {
  1206. parent.target!.canvasModel.moveDesktopMouse(x, y);
  1207. }
  1208. return _handlePointerDevicePos(
  1209. kind,
  1210. x,
  1211. y,
  1212. isMove,
  1213. canvas,
  1214. rect,
  1215. evtType,
  1216. onExit: onExit,
  1217. buttons: buttons,
  1218. );
  1219. }
  1220. bool _isInCurrentWindow(double x, double y) {
  1221. final w = _windowRect!.width / devicePixelRatio;
  1222. final h = _windowRect!.width / devicePixelRatio;
  1223. return x >= 0 && y >= 0 && x <= w && y <= h;
  1224. }
  1225. static RemoteWindowCoords? findRemoteCoords(double x, double y,
  1226. List<RemoteWindowCoords> remoteWindowCoords, double devicePixelRatio) {
  1227. x *= devicePixelRatio;
  1228. y *= devicePixelRatio;
  1229. for (final c in remoteWindowCoords) {
  1230. if (x >= c.relativeOffset.dx &&
  1231. y >= c.relativeOffset.dy &&
  1232. x <= c.relativeOffset.dx + c.windowRect.width &&
  1233. y <= c.relativeOffset.dy + c.windowRect.height) {
  1234. return c;
  1235. }
  1236. }
  1237. return null;
  1238. }
  1239. Point? _handlePointerDevicePos(
  1240. String kind,
  1241. double x,
  1242. double y,
  1243. bool moveInCanvas,
  1244. CanvasCoords canvas,
  1245. Rect? rect,
  1246. String evtType, {
  1247. bool onExit = false,
  1248. int buttons = kPrimaryMouseButton,
  1249. }) {
  1250. if (rect == null) {
  1251. return null;
  1252. }
  1253. final nearThr = 3;
  1254. var nearRight = (canvas.size.width - x) < nearThr;
  1255. var nearBottom = (canvas.size.height - y) < nearThr;
  1256. final imageWidth = rect.width * canvas.scale;
  1257. final imageHeight = rect.height * canvas.scale;
  1258. if (canvas.scrollStyle == ScrollStyle.scrollbar) {
  1259. x += imageWidth * canvas.scrollX;
  1260. y += imageHeight * canvas.scrollY;
  1261. // boxed size is a center widget
  1262. if (canvas.size.width > imageWidth) {
  1263. x -= ((canvas.size.width - imageWidth) / 2);
  1264. }
  1265. if (canvas.size.height > imageHeight) {
  1266. y -= ((canvas.size.height - imageHeight) / 2);
  1267. }
  1268. } else {
  1269. x -= canvas.x;
  1270. y -= canvas.y;
  1271. }
  1272. x /= canvas.scale;
  1273. y /= canvas.scale;
  1274. if (canvas.scale > 0 && canvas.scale < 1) {
  1275. final step = 1.0 / canvas.scale - 1;
  1276. if (nearRight) {
  1277. x += step;
  1278. }
  1279. if (nearBottom) {
  1280. y += step;
  1281. }
  1282. }
  1283. x += rect.left;
  1284. y += rect.top;
  1285. if (onExit) {
  1286. final pos = setNearestEdge(x, y, rect);
  1287. x = pos.dx;
  1288. y = pos.dy;
  1289. }
  1290. return InputModel.getPointInRemoteRect(
  1291. true, peerPlatform, kind, evtType, x, y, rect,
  1292. buttons: buttons);
  1293. }
  1294. static Point<double>? getPointInRemoteRect(
  1295. bool isLocalDesktop,
  1296. String? peerPlatform,
  1297. String kind,
  1298. String evtType,
  1299. double evtX,
  1300. double evtY,
  1301. Rect rect,
  1302. {int buttons = kPrimaryMouseButton}) {
  1303. double minX = rect.left;
  1304. // https://github.com/rustdesk/rustdesk/issues/6678
  1305. // For Windows, [0,maxX], [0,maxY] should be set to enable window snapping.
  1306. double maxX = (rect.left + rect.width) -
  1307. (peerPlatform == kPeerPlatformWindows ? 0 : 1);
  1308. double minY = rect.top;
  1309. double maxY = (rect.top + rect.height) -
  1310. (peerPlatform == kPeerPlatformWindows ? 0 : 1);
  1311. evtX = InputModel.tryGetNearestRange(evtX, minX, maxX, 5);
  1312. evtY = InputModel.tryGetNearestRange(evtY, minY, maxY, 5);
  1313. if (isLocalDesktop) {
  1314. if (kind == kPointerEventKindMouse) {
  1315. if (evtX < minX || evtY < minY || evtX > maxX || evtY > maxY) {
  1316. // If left mouse up, no early return.
  1317. if (!(buttons == kPrimaryMouseButton &&
  1318. evtType == kMouseEventTypeUp)) {
  1319. return null;
  1320. }
  1321. }
  1322. }
  1323. } else {
  1324. bool evtXInRange = evtX >= minX && evtX <= maxX;
  1325. bool evtYInRange = evtY >= minY && evtY <= maxY;
  1326. if (!(evtXInRange || evtYInRange)) {
  1327. return null;
  1328. }
  1329. if (evtX < minX) {
  1330. evtX = minX;
  1331. } else if (evtX > maxX) {
  1332. evtX = maxX;
  1333. }
  1334. if (evtY < minY) {
  1335. evtY = minY;
  1336. } else if (evtY > maxY) {
  1337. evtY = maxY;
  1338. }
  1339. }
  1340. return Point(evtX, evtY);
  1341. }
  1342. /// Web only
  1343. void listenToMouse(bool yesOrNo) {
  1344. if (yesOrNo) {
  1345. platformFFI.startDesktopWebListener();
  1346. } else {
  1347. platformFFI.stopDesktopWebListener();
  1348. }
  1349. }
  1350. void onMobileBack() {
  1351. final minBackButtonVersion = "1.3.8";
  1352. final peerVersion =
  1353. parent.target?.ffiModel.pi.version ?? minBackButtonVersion;
  1354. var btn = MouseButtons.back;
  1355. // For compatibility with old versions
  1356. if (versionCmp(peerVersion, minBackButtonVersion) < 0) {
  1357. btn = MouseButtons.right;
  1358. }
  1359. tap(btn);
  1360. }
  1361. void onMobileHome() => tap(MouseButtons.wheel);
  1362. Future<void> onMobileApps() async {
  1363. sendMouse('down', MouseButtons.wheel);
  1364. await Future.delayed(const Duration(milliseconds: 500));
  1365. sendMouse('up', MouseButtons.wheel);
  1366. }
  1367. // Simulate a key press event.
  1368. // `usbHidUsage` is the USB HID usage code of the key.
  1369. Future<void> tapHidKey(int usbHidUsage) async {
  1370. newKeyboardMode(kKeyFlutterKey, usbHidUsage, true);
  1371. await Future.delayed(Duration(milliseconds: 100));
  1372. newKeyboardMode(kKeyFlutterKey, usbHidUsage, false);
  1373. }
  1374. Future<void> onMobileVolumeUp() async =>
  1375. await tapHidKey(PhysicalKeyboardKey.audioVolumeUp.usbHidUsage & 0xFFFF);
  1376. Future<void> onMobileVolumeDown() async =>
  1377. await tapHidKey(PhysicalKeyboardKey.audioVolumeDown.usbHidUsage & 0xFFFF);
  1378. Future<void> onMobilePower() async =>
  1379. await tapHidKey(PhysicalKeyboardKey.power.usbHidUsage & 0xFFFF);
  1380. }