input_model.dart 41 KB

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