2022-11-20 17:46:27 +03:00
import ' dart:async ' ;
2022-09-27 15:35:02 +03:00
import ' dart:convert ' ;
2023-06-07 15:01:01 +03:00
import ' dart:io ' ;
2022-09-27 15:35:02 +03:00
import ' dart:math ' ;
2022-11-02 17:23:23 +03:00
import ' dart:ui ' as ui ;
2022-09-27 15:35:02 +03:00
2022-09-15 09:49:59 +03:00
import ' package:flutter/gestures.dart ' ;
import ' package:flutter/services.dart ' ;
import ' package:flutter/widgets.dart ' ;
2022-09-27 15:35:02 +03:00
import ' package:get/get.dart ' ;
2022-09-15 09:49:59 +03:00
import ' ../../models/model.dart ' ;
import ' ../../models/platform_model.dart ' ;
2022-09-27 15:35:02 +03:00
import ' ../common.dart ' ;
2022-09-15 09:49:59 +03:00
import ' ../consts.dart ' ;
2022-11-01 12:01:43 +03:00
import ' ./state_model.dart ' ;
2022-09-15 09:49:59 +03:00
2022-09-27 15:35:02 +03:00
/// Mouse button enum.
enum MouseButtons { left , right , wheel }
2022-09-15 09:49:59 +03:00
2023-01-04 15:06:48 +03:00
const _kMouseEventDown = ' mousedown ' ;
const _kMouseEventUp = ' mouseup ' ;
const _kMouseEventMove = ' mousemove ' ;
2022-09-27 15:35:02 +03:00
extension ToString on MouseButtons {
String get value {
switch ( this ) {
case MouseButtons . left:
return ' left ' ;
case MouseButtons . right:
return ' right ' ;
case MouseButtons . wheel:
return ' wheel ' ;
}
2022-09-15 09:49:59 +03:00
}
2022-09-27 15:35:02 +03:00
}
2023-08-09 18:42:53 +03:00
class PointerEventToRust {
final String kind ;
final String type ;
final dynamic value ;
PointerEventToRust ( this . kind , this . type , this . value ) ;
Map < String , dynamic > toJson ( ) {
return {
' k ' : kind ,
' v ' : {
' t ' : type ,
' v ' : value ,
}
} ;
}
}
2022-09-27 15:35:02 +03:00
class InputModel {
final WeakReference < FFI > parent ;
String keyboardMode = " legacy " ;
// keyboard
var shift = false ;
var ctrl = false ;
var alt = false ;
var command = false ;
2022-11-20 17:46:27 +03:00
// trackpad
2023-03-20 13:42:03 +03:00
var _trackpadLastDelta = Offset . zero ;
var _stopFling = true ;
2023-06-07 15:01:01 +03:00
var _fling = false ;
2022-11-20 17:46:27 +03:00
Timer ? _flingTimer ;
2023-06-07 15:01:01 +03:00
final _flingBaseDelay = 30 ;
2023-06-28 18:00:29 +03:00
// trackpad, peer linux
final _trackpadSpeed = 0.06 ;
var _trackpadScrollUnsent = Offset . zero ;
2022-11-20 17:46:27 +03:00
2023-07-18 16:04:12 +03:00
var _lastScale = 1.0 ;
2022-09-27 15:35:02 +03:00
// mouse
2022-09-27 17:16:27 +03:00
final isPhysicalMouse = false . obs ;
2023-01-05 09:58:38 +03:00
int _lastButtons = 0 ;
2022-11-14 10:05:44 +03:00
Offset lastMousePos = Offset . zero ;
2022-09-27 15:35:02 +03:00
2023-06-06 02:39:44 +03:00
late final SessionID sessionId ;
2023-03-28 05:52:43 +03:00
bool get keyboardPerm = > parent . target ! . ffiModel . keyboard ;
2023-08-09 02:39:16 +03:00
String get id = > parent . target ? . id ? ? ' ' ;
String ? get peerPlatform = > parent . target ? . ffiModel . pi . platform ;
2023-03-28 05:52:43 +03:00
2023-06-06 02:39:44 +03:00
InputModel ( this . parent ) {
sessionId = parent . target ! . sessionId ;
}
2022-09-15 09:49:59 +03:00
KeyEventResult handleRawKeyEvent ( FocusNode data , RawKeyEvent e ) {
2023-04-07 11:03:18 +03:00
if ( isDesktop & & ! stateGlobal . grabKeyboard ) {
2023-02-21 13:43:43 +03:00
return KeyEventResult . handled ;
}
2023-02-09 17:14:24 +03:00
// * Currently mobile does not enable map mode
if ( isDesktop ) {
2023-06-06 02:39:44 +03:00
bind . sessionGetKeyboardMode ( sessionId: sessionId ) . then ( ( result ) {
2023-02-09 17:14:24 +03:00
keyboardMode = result . toString ( ) ;
} ) ;
}
2022-09-15 09:49:59 +03:00
2022-09-27 06:01:01 +03:00
final key = e . logicalKey ;
if ( e is RawKeyDownEvent ) {
2022-09-27 15:35:02 +03:00
if ( ! e . repeat ) {
if ( e . isAltPressed & & ! alt ) {
alt = true ;
} else if ( e . isControlPressed & & ! ctrl ) {
ctrl = true ;
} else if ( e . isShiftPressed & & ! shift ) {
shift = true ;
} else if ( e . isMetaPressed & & ! command ) {
command = true ;
2022-09-27 06:01:01 +03:00
}
}
}
if ( e is RawKeyUpEvent ) {
if ( key = = LogicalKeyboardKey . altLeft | |
key = = LogicalKeyboardKey . altRight ) {
2022-09-27 15:35:02 +03:00
alt = false ;
2022-09-27 06:01:01 +03:00
} else if ( key = = LogicalKeyboardKey . controlLeft | |
key = = LogicalKeyboardKey . controlRight ) {
2022-09-27 15:35:02 +03:00
ctrl = false ;
2022-09-27 06:01:01 +03:00
} else if ( key = = LogicalKeyboardKey . shiftRight | |
key = = LogicalKeyboardKey . shiftLeft ) {
2022-09-27 15:35:02 +03:00
shift = false ;
2022-09-27 06:01:01 +03:00
} else if ( key = = LogicalKeyboardKey . metaLeft | |
key = = LogicalKeyboardKey . metaRight | |
key = = LogicalKeyboardKey . superKey ) {
2022-09-27 15:35:02 +03:00
command = false ;
2022-09-27 06:01:01 +03:00
}
}
2023-02-09 17:14:24 +03:00
// * Currently mobile does not enable map mode
if ( isDesktop & & keyboardMode = = ' map ' ) {
2022-09-15 09:49:59 +03:00
mapKeyboardMode ( e ) ;
} else {
legacyKeyboardMode ( e ) ;
}
return KeyEventResult . handled ;
}
void mapKeyboardMode ( RawKeyEvent e ) {
2023-04-04 13:35:01 +03:00
int positionCode = - 1 ;
int platformCode = - 1 ;
2022-09-15 09:49:59 +03:00
bool down ;
if ( e . data is RawKeyEventDataMacOs ) {
RawKeyEventDataMacOs newData = e . data as RawKeyEventDataMacOs ;
2023-04-04 13:35:01 +03:00
positionCode = newData . keyCode ;
platformCode = newData . keyCode ;
2022-09-15 09:49:59 +03:00
} else if ( e . data is RawKeyEventDataWindows ) {
RawKeyEventDataWindows newData = e . data as RawKeyEventDataWindows ;
2023-04-04 13:35:01 +03:00
positionCode = newData . scanCode ;
platformCode = newData . keyCode ;
2022-09-15 09:49:59 +03:00
} else if ( e . data is RawKeyEventDataLinux ) {
RawKeyEventDataLinux newData = e . data as RawKeyEventDataLinux ;
2023-01-10 09:11:49 +03:00
// scanCode and keyCode of RawKeyEventDataLinux are incorrect.
// 1. scanCode means keycode
// 2. keyCode means keysym
2023-04-04 13:35:01 +03:00
positionCode = newData . scanCode ;
platformCode = newData . keyCode ;
2022-09-29 13:56:47 +03:00
} else if ( e . data is RawKeyEventDataAndroid ) {
2022-09-28 10:12:08 +03:00
RawKeyEventDataAndroid newData = e . data as RawKeyEventDataAndroid ;
2023-04-04 13:35:01 +03:00
positionCode = newData . scanCode + 8 ;
platformCode = newData . keyCode ;
} else { }
2022-09-15 09:49:59 +03:00
if ( e is RawKeyDownEvent ) {
down = true ;
} else {
down = false ;
}
2023-04-04 13:35:01 +03:00
inputRawKey ( e . character ? ? ' ' , platformCode , positionCode , down ) ;
2022-09-27 15:35:02 +03:00
}
2022-09-15 09:49:59 +03:00
2022-09-27 15:35:02 +03:00
/// Send raw Key Event
2023-04-04 13:35:01 +03:00
void inputRawKey ( String name , int platformCode , int positionCode , bool down ) {
2023-01-10 09:11:49 +03:00
const capslock = 1 ;
const numlock = 2 ;
const scrolllock = 3 ;
int lockModes = 0 ;
if ( HardwareKeyboard . instance . lockModesEnabled
. contains ( KeyboardLockMode . capsLock ) ) {
lockModes | = ( 1 < < capslock ) ;
}
if ( HardwareKeyboard . instance . lockModesEnabled
. contains ( KeyboardLockMode . numLock ) ) {
lockModes | = ( 1 < < numlock ) ;
}
if ( HardwareKeyboard . instance . lockModesEnabled
. contains ( KeyboardLockMode . scrollLock ) ) {
lockModes | = ( 1 < < scrolllock ) ;
}
2022-09-27 15:35:02 +03:00
bind . sessionHandleFlutterKeyEvent (
2023-06-06 02:39:44 +03:00
sessionId: sessionId ,
2022-09-27 15:35:02 +03:00
name: name ,
2023-04-04 13:35:01 +03:00
platformCode: platformCode ,
positionCode: positionCode ,
2023-01-10 09:11:49 +03:00
lockModes: lockModes ,
2022-09-27 15:35:02 +03:00
downOrUp: down ) ;
2022-09-15 09:49:59 +03:00
}
void legacyKeyboardMode ( RawKeyEvent e ) {
if ( e is RawKeyDownEvent ) {
if ( e . repeat ) {
sendRawKey ( e , press: true ) ;
} else {
sendRawKey ( e , down: true ) ;
}
}
if ( e is RawKeyUpEvent ) {
sendRawKey ( e ) ;
}
}
void sendRawKey ( RawKeyEvent e , { bool ? down , bool ? press } ) {
// for maximum compatibility
2022-09-26 10:50:12 +03:00
final label = physicalKeyMap [ e . physicalKey . usbHidUsage ] ? ?
logicalKeyMap [ e . logicalKey . keyId ] ? ?
2022-09-15 09:49:59 +03:00
e . logicalKey . keyLabel ;
2022-09-27 15:35:02 +03:00
inputKey ( label , down: down , press: press ? ? false ) ;
2022-09-15 09:49:59 +03:00
}
2022-09-27 15:35:02 +03:00
/// Send key stroke event.
/// [down] indicates the key's state(down or up).
/// [press] indicates a click event(down and up).
void inputKey ( String name , { bool ? down , bool ? press } ) {
2023-03-28 05:52:43 +03:00
if ( ! keyboardPerm ) return ;
2022-09-27 15:35:02 +03:00
bind . sessionInputKey (
2023-06-06 02:39:44 +03:00
sessionId: sessionId ,
2022-09-27 15:35:02 +03:00
name: name ,
down: down ? ? false ,
press: press ? ? true ,
alt: alt ,
ctrl: ctrl ,
shift: shift ,
command: command ) ;
2022-09-15 09:49:59 +03:00
}
2023-08-08 18:53:53 +03:00
Map < String , dynamic > _getMouseEvent ( PointerEvent evt , String type ) {
2022-09-15 09:49:59 +03:00
final Map < String , dynamic > out = { } ;
2023-01-04 15:06:48 +03:00
// Check update event type and set buttons to be sent.
2023-01-05 09:58:38 +03:00
int buttons = _lastButtons ;
2023-01-04 15:06:48 +03:00
if ( type = = _kMouseEventMove ) {
2023-01-09 10:30:18 +03:00
// flutter may emit move event if one button is pressed and another button
2023-01-04 15:06:48 +03:00
// is pressing or releasing.
2023-01-05 09:58:38 +03:00
if ( evt . buttons ! = _lastButtons ) {
2023-01-04 15:06:48 +03:00
// For simplicity
// Just consider 3 - 1 ((Left + Right buttons) - Left button)
// Do not consider 2 - 1 (Right button - Left button)
// or 6 - 5 ((Right + Mid buttons) - (Left + Mid buttons))
// and so on
2023-01-05 09:58:38 +03:00
buttons = evt . buttons - _lastButtons ;
2023-01-04 15:06:48 +03:00
if ( buttons > 0 ) {
type = _kMouseEventDown ;
} else {
type = _kMouseEventUp ;
buttons = - buttons ;
}
}
2022-09-15 09:49:59 +03:00
} else {
2023-01-04 15:06:48 +03:00
if ( evt . buttons ! = 0 ) {
buttons = evt . buttons ;
}
2022-09-15 09:49:59 +03:00
}
2023-01-05 09:58:38 +03:00
_lastButtons = evt . buttons ;
2023-01-04 15:06:48 +03:00
out [ ' buttons ' ] = buttons ;
out [ ' type ' ] = type ;
2022-09-15 09:49:59 +03:00
return out ;
}
2022-09-27 15:35:02 +03:00
/// Send a mouse tap event(down and up).
void tap ( MouseButtons button ) {
sendMouse ( ' down ' , button ) ;
sendMouse ( ' up ' , button ) ;
}
2023-07-17 15:07:55 +03:00
void tapDown ( MouseButtons button ) {
sendMouse ( ' down ' , button ) ;
}
2023-07-18 16:04:12 +03:00
void tapUp ( MouseButtons button ) {
2023-07-17 15:07:55 +03:00
sendMouse ( ' up ' , button ) ;
}
2022-09-27 15:35:02 +03:00
/// Send scroll event with scroll distance [y].
void scroll ( int y ) {
bind . sessionSendMouse (
2023-06-06 02:39:44 +03:00
sessionId: sessionId ,
2022-09-27 15:35:02 +03:00
msg: json
. encode ( modify ( { ' id ' : id , ' type ' : ' wheel ' , ' y ' : y . toString ( ) } ) ) ) ;
}
/// Reset key modifiers to false, including [shift], [ctrl], [alt] and [command].
void resetModifiers ( ) {
shift = ctrl = alt = command = false ;
}
/// Modify the given modifier map [evt] based on current modifier key status.
2023-08-08 18:53:53 +03:00
Map < String , dynamic > modify ( Map < String , dynamic > evt ) {
2022-09-27 15:35:02 +03:00
if ( ctrl ) evt [ ' ctrl ' ] = ' true ' ;
if ( shift ) evt [ ' shift ' ] = ' true ' ;
if ( alt ) evt [ ' alt ' ] = ' true ' ;
if ( command ) evt [ ' command ' ] = ' true ' ;
return evt ;
}
/// Send mouse press event.
void sendMouse ( String type , MouseButtons button ) {
2023-03-28 05:52:43 +03:00
if ( ! keyboardPerm ) return ;
2022-09-27 15:35:02 +03:00
bind . sessionSendMouse (
2023-06-06 02:39:44 +03:00
sessionId: sessionId ,
2022-09-27 15:35:02 +03:00
msg: json . encode ( modify ( { ' type ' : type , ' buttons ' : button . value } ) ) ) ;
}
void enterOrLeave ( bool enter ) {
// Fix status
if ( ! enter ) {
resetModifiers ( ) ;
}
2022-11-20 17:46:27 +03:00
_flingTimer ? . cancel ( ) ;
2023-06-06 02:39:44 +03:00
bind . sessionEnterOrLeave ( sessionId: sessionId , enter: enter ) ;
2022-09-27 15:35:02 +03:00
}
/// Send mouse movement event with distance in [x] and [y].
void moveMouse ( double x , double y ) {
2023-03-28 05:52:43 +03:00
if ( ! keyboardPerm ) return ;
2022-09-27 15:35:02 +03:00
var x2 = x . toInt ( ) ;
var y2 = y . toInt ( ) ;
bind . sessionSendMouse (
2023-06-06 02:39:44 +03:00
sessionId: sessionId ,
msg: json . encode ( modify ( { ' x ' : ' $ x2 ' , ' y ' : ' $ y2 ' } ) ) ) ;
2022-09-27 15:35:02 +03:00
}
2022-09-15 09:49:59 +03:00
void onPointHoverImage ( PointerHoverEvent e ) {
2023-03-20 13:42:03 +03:00
_stopFling = true ;
2023-05-04 15:31:03 +03:00
if ( e . kind ! = ui . PointerDeviceKind . mouse ) return ;
2022-09-27 17:16:27 +03:00
if ( ! isPhysicalMouse . value ) {
isPhysicalMouse . value = true ;
2022-09-15 09:49:59 +03:00
}
2022-09-27 17:16:27 +03:00
if ( isPhysicalMouse . value ) {
2023-08-08 18:53:53 +03:00
handleMouse ( _getMouseEvent ( e , _kMouseEventMove ) , e . position ) ;
2022-09-15 09:49:59 +03:00
}
}
2023-03-20 13:42:03 +03:00
void onPointerPanZoomStart ( PointerPanZoomStartEvent e ) {
2023-07-18 16:04:12 +03:00
_lastScale = 1.0 ;
2023-03-20 13:42:03 +03:00
_stopFling = true ;
2023-08-09 02:39:16 +03:00
if ( peerPlatform = = kPeerPlatformAndroid ) {
handlePointerEvent ( ' touch ' , ' pan_start ' , e . position ) ;
}
2023-03-20 13:42:03 +03:00
}
2022-11-20 17:46:27 +03:00
// https://docs.flutter.dev/release/breaking-changes/trackpad-gestures
void onPointerPanZoomUpdate ( PointerPanZoomUpdateEvent e ) {
2023-08-09 02:39:16 +03:00
if ( peerPlatform ! = kPeerPlatformAndroid ) {
final scale = ( ( e . scale - _lastScale ) * 1000 ) . toInt ( ) ;
_lastScale = e . scale ;
if ( scale ! = 0 ) {
bind . sessionSendPointer (
sessionId: sessionId ,
2023-08-09 18:42:53 +03:00
msg: json . encode (
PointerEventToRust ( kPointerEventKindTouch , ' scale ' , scale )
. toJson ( ) ) ) ;
2023-08-09 02:39:16 +03:00
return ;
}
2023-07-18 20:18:10 +03:00
}
2023-06-28 18:06:01 +03:00
final delta = e . panDelta ;
2023-03-20 13:42:03 +03:00
_trackpadLastDelta = delta ;
2023-06-28 18:00:29 +03:00
2023-06-07 15:01:01 +03:00
var x = delta . dx . toInt ( ) ;
var y = delta . dy . toInt ( ) ;
2023-08-09 02:39:16 +03:00
if ( peerPlatform = = kPeerPlatformLinux | |
peerPlatform = = kPeerPlatformAndroid ) {
2023-06-28 18:00:29 +03:00
_trackpadScrollUnsent + = ( delta * _trackpadSpeed ) ;
x = _trackpadScrollUnsent . dx . truncate ( ) ;
y = _trackpadScrollUnsent . dy . truncate ( ) ;
_trackpadScrollUnsent - = Offset ( x . toDouble ( ) , y . toDouble ( ) ) ;
} else {
if ( x = = 0 & & y = = 0 ) {
final thr = 0.1 ;
if ( delta . dx . abs ( ) > delta . dy . abs ( ) ) {
x = delta . dx > thr ? 1 : ( delta . dx < - thr ? - 1 : 0 ) ;
} else {
y = delta . dy > thr ? 1 : ( delta . dy < - thr ? - 1 : 0 ) ;
}
2023-06-28 17:35:21 +03:00
}
}
2023-06-07 15:01:01 +03:00
if ( x ! = 0 | | y ! = 0 ) {
2023-08-09 02:39:16 +03:00
if ( peerPlatform = = kPeerPlatformAndroid ) {
2023-08-09 18:42:53 +03:00
handlePointerEvent ( ' touch ' , ' pan_update ' , Offset ( x . toDouble ( ) , y . toDouble ( ) ) ) ;
2023-08-09 02:39:16 +03:00
} else {
bind . sessionSendMouse (
sessionId: sessionId ,
msg: ' {"type": "trackpad", "x": " $ x ", "y": " $ y "} ' ) ;
}
2023-06-07 15:01:01 +03:00
}
2022-11-20 17:46:27 +03:00
}
2023-05-21 18:24:44 +03:00
void _scheduleFling ( double x , double y , int delay ) {
2023-03-28 05:52:43 +03:00
if ( ( x = = 0 & & y = = 0 ) | | _stopFling ) {
2023-06-07 15:01:01 +03:00
_fling = false ;
2023-03-20 13:42:03 +03:00
return ;
}
_flingTimer = Timer ( Duration ( milliseconds: delay ) , ( ) {
if ( _stopFling ) {
2023-06-07 15:01:01 +03:00
_fling = false ;
2023-03-20 13:42:03 +03:00
return ;
}
2023-06-07 15:01:01 +03:00
final d = 0.97 ;
2023-03-20 13:42:03 +03:00
x * = d ;
y * = d ;
2023-03-28 05:52:43 +03:00
// Try set delta (x,y) and delay.
2023-06-07 15:01:01 +03:00
var dx = x . toInt ( ) ;
var dy = y . toInt ( ) ;
2023-06-28 18:00:29 +03:00
if ( parent . target ? . ffiModel . pi . platform = = kPeerPlatformLinux ) {
dx = ( x * _trackpadSpeed ) . toInt ( ) ;
dy = ( y * _trackpadSpeed ) . toInt ( ) ;
}
2023-03-20 13:42:03 +03:00
var delay = _flingBaseDelay ;
if ( dx = = 0 & & dy = = 0 ) {
2023-06-07 15:01:01 +03:00
_fling = false ;
2023-03-20 13:42:03 +03:00
return ;
}
bind . sessionSendMouse (
2023-06-06 02:39:44 +03:00
sessionId: sessionId ,
msg: ' {"type": "trackpad", "x": " $ dx ", "y": " $ dy "} ' ) ;
2023-05-21 18:24:44 +03:00
_scheduleFling ( x , y , delay ) ;
2023-03-20 13:42:03 +03:00
} ) ;
}
2023-06-07 15:01:01 +03:00
void waitLastFlingDone ( ) {
if ( _fling ) {
_stopFling = true ;
}
for ( var i = 0 ; i < 5 ; i + + ) {
if ( ! _fling ) {
break ;
}
sleep ( Duration ( milliseconds: 10 ) ) ;
}
_flingTimer ? . cancel ( ) ;
}
2022-11-20 17:46:27 +03:00
void onPointerPanZoomEnd ( PointerPanZoomEndEvent e ) {
2023-08-09 02:39:16 +03:00
if ( peerPlatform = = kPeerPlatformAndroid ) {
handlePointerEvent ( ' touch ' , ' pan_end ' , e . position ) ;
return ;
}
2023-07-18 20:18:10 +03:00
bind . sessionSendPointer (
sessionId: sessionId ,
2023-08-09 18:42:53 +03:00
msg: json . encode (
PointerEventToRust ( kPointerEventKindTouch , ' scale ' , 0 ) . toJson ( ) ) ) ;
2023-07-18 20:18:10 +03:00
2023-06-07 15:01:01 +03:00
waitLastFlingDone ( ) ;
2023-03-20 13:42:03 +03:00
_stopFling = false ;
2023-06-07 15:01:01 +03:00
2023-05-21 18:24:44 +03:00
// 2.0 is an experience value
double minFlingValue = 2.0 ;
if ( _trackpadLastDelta . dx . abs ( ) > minFlingValue | |
_trackpadLastDelta . dy . abs ( ) > minFlingValue ) {
2023-06-07 15:01:01 +03:00
_fling = true ;
2023-05-21 18:24:44 +03:00
_scheduleFling (
_trackpadLastDelta . dx , _trackpadLastDelta . dy , _flingBaseDelay ) ;
}
2023-03-20 13:42:03 +03:00
_trackpadLastDelta = Offset . zero ;
2022-11-20 17:46:27 +03:00
}
2023-02-01 11:29:13 +03:00
2022-09-15 09:49:59 +03:00
void onPointDownImage ( PointerDownEvent e ) {
2023-07-17 15:07:55 +03:00
debugPrint ( " onPointDownImage ${ e . kind } " ) ;
2023-03-20 13:42:03 +03:00
_stopFling = true ;
2023-05-04 15:31:03 +03:00
if ( e . kind ! = ui . PointerDeviceKind . mouse ) {
2022-09-27 17:16:27 +03:00
if ( isPhysicalMouse . value ) {
isPhysicalMouse . value = false ;
2022-09-15 09:49:59 +03:00
}
}
2022-09-27 17:16:27 +03:00
if ( isPhysicalMouse . value ) {
2023-08-08 18:53:53 +03:00
handleMouse ( _getMouseEvent ( e , _kMouseEventDown ) , e . position ) ;
2022-09-15 09:49:59 +03:00
}
}
void onPointUpImage ( PointerUpEvent e ) {
2023-05-04 15:31:03 +03:00
if ( e . kind ! = ui . PointerDeviceKind . mouse ) return ;
2022-09-27 17:16:27 +03:00
if ( isPhysicalMouse . value ) {
2023-08-08 18:53:53 +03:00
handleMouse ( _getMouseEvent ( e , _kMouseEventUp ) , e . position ) ;
2022-09-15 09:49:59 +03:00
}
}
void onPointMoveImage ( PointerMoveEvent e ) {
if ( e . kind ! = ui . PointerDeviceKind . mouse ) return ;
2022-09-27 17:16:27 +03:00
if ( isPhysicalMouse . value ) {
2023-08-08 18:53:53 +03:00
handleMouse ( _getMouseEvent ( e , _kMouseEventMove ) , e . position ) ;
2022-09-15 09:49:59 +03:00
}
}
void onPointerSignalImage ( PointerSignalEvent e ) {
if ( e is PointerScrollEvent ) {
var dx = e . scrollDelta . dx . toInt ( ) ;
var dy = e . scrollDelta . dy . toInt ( ) ;
if ( dx > 0 ) {
dx = - 1 ;
} else if ( dx < 0 ) {
dx = 1 ;
}
if ( dy > 0 ) {
dy = - 1 ;
} else if ( dy < 0 ) {
dy = 1 ;
}
bind . sessionSendMouse (
2023-06-06 02:39:44 +03:00
sessionId: sessionId ,
msg: ' {"type": "wheel", "x": " $ dx ", "y": " $ dy "} ' ) ;
2022-09-27 15:35:02 +03:00
}
}
2023-01-30 17:12:36 +03:00
void refreshMousePos ( ) = > handleMouse ( {
' buttons ' : 0 ,
' type ' : _kMouseEventMove ,
2023-08-08 18:53:53 +03:00
} , lastMousePos ) ;
2023-01-30 17:12:36 +03:00
2023-03-13 16:05:41 +03:00
void tryMoveEdgeOnExit ( Offset pos ) = > handleMouse (
2023-03-13 16:03:43 +03:00
{
' buttons ' : 0 ,
' type ' : _kMouseEventMove ,
} ,
2023-08-08 18:53:53 +03:00
pos ,
2023-03-13 16:03:43 +03:00
onExit: true ,
) ;
int trySetNearestRange ( int v , int min , int max , int n ) {
if ( v < min & & v > = min - n ) {
v = min ;
}
if ( v > max & & v < = max + n ) {
v = max ;
}
return v ;
}
Offset setNearestEdge ( double x , double y , Display d ) {
double left = x - d . x ;
double right = d . x + d . width - 1 - x ;
double top = y - d . y ;
double bottom = d . y + d . height - 1 - y ;
if ( left < right & & left < top & & left < bottom ) {
x = d . x ;
}
if ( right < left & & right < top & & right < bottom ) {
x = d . x + d . width - 1 ;
}
if ( top < left & & top < right & & top < bottom ) {
y = d . y ;
}
if ( bottom < left & & bottom < right & & bottom < top ) {
y = d . y + d . height - 1 ;
}
return Offset ( x , y ) ;
}
2023-08-09 02:39:16 +03:00
void handlePointerEvent ( String kind , String type , Offset offset ) {
double x = offset . dx ;
2023-08-09 18:42:53 +03:00
double y = offset . dy ;
2023-08-09 02:39:16 +03:00
if ( _checkPeerControlProtected ( x , y ) ) {
2023-08-08 18:53:53 +03:00
return ;
}
// Only touch events are handled for now. So we can just ignore buttons.
// to-do: handle mouse events
2023-08-09 02:39:16 +03:00
2023-08-09 18:42:53 +03:00
late final dynamic evtValue ;
if ( type = = ' pan_update ' ) {
evtValue = {
' x ' : ' ${ x . toInt ( ) } ' ,
' y ' : ' ${ y . toInt ( ) } ' ,
} ;
} else {
final isMoveTypes = [ ' pan_start ' , ' pan_end ' ] ;
final pos = handlePointerDevicePos (
kPointerEventKindTouch ,
x ,
y ,
isMoveTypes . contains ( type ) ,
type ,
) ;
if ( pos = = null ) {
return ;
}
evtValue = {
' x ' : ' ${ pos . x } ' ,
' y ' : ' ${ pos . y } ' ,
} ;
2023-08-09 02:39:16 +03:00
}
2023-08-09 18:42:53 +03:00
final evt = PointerEventToRust ( kind , type , evtValue ) . toJson ( ) ;
2023-08-09 02:39:16 +03:00
bind . sessionSendPointer (
2023-08-09 18:42:53 +03:00
sessionId: sessionId , msg: json . encode ( modify ( evt ) ) ) ;
2023-08-08 18:53:53 +03:00
}
2022-11-08 07:01:51 +03:00
2023-08-09 02:39:16 +03:00
bool _checkPeerControlProtected ( double x , double y ) {
2023-08-08 18:53:53 +03:00
final cursorModel = parent . target ! . cursorModel ;
2022-11-14 10:05:44 +03:00
if ( cursorModel . isPeerControlProtected ) {
lastMousePos = ui . Offset ( x , y ) ;
2023-08-08 18:53:53 +03:00
return true ;
2022-11-08 08:37:08 +03:00
}
2022-11-14 10:05:44 +03:00
if ( ! cursorModel . gotMouseControl ) {
bool selfGetControl =
( x - lastMousePos . dx ) . abs ( ) > kMouseControlDistance | |
( y - lastMousePos . dy ) . abs ( ) > kMouseControlDistance ;
if ( selfGetControl ) {
cursorModel . gotMouseControl = true ;
2022-11-08 07:01:51 +03:00
} else {
2022-11-14 10:05:44 +03:00
lastMousePos = ui . Offset ( x , y ) ;
2023-08-08 18:53:53 +03:00
return true ;
2022-11-08 07:01:51 +03:00
}
}
2022-11-14 10:05:44 +03:00
lastMousePos = ui . Offset ( x , y ) ;
2023-08-08 18:53:53 +03:00
return false ;
}
void handleMouse (
Map < String , dynamic > evt ,
Offset offset , {
bool onExit = false ,
} ) {
double x = offset . dx ;
double y = max ( 0.0 , offset . dy ) ;
2023-08-09 02:39:16 +03:00
if ( _checkPeerControlProtected ( x , y ) ) {
2023-08-08 18:53:53 +03:00
return ;
}
2022-11-08 07:01:51 +03:00
2022-09-27 15:35:02 +03:00
var type = ' ' ;
var isMove = false ;
switch ( evt [ ' type ' ] ) {
2023-01-04 15:06:48 +03:00
case _kMouseEventDown:
2022-09-27 15:35:02 +03:00
type = ' down ' ;
break ;
2023-01-04 15:06:48 +03:00
case _kMouseEventUp:
2022-09-27 15:35:02 +03:00
type = ' up ' ;
break ;
2023-01-04 15:06:48 +03:00
case _kMouseEventMove:
2022-09-27 15:35:02 +03:00
isMove = true ;
break ;
default :
return ;
}
evt [ ' type ' ] = type ;
2023-08-06 09:00:48 +03:00
final pos = handlePointerDevicePos (
2023-08-09 18:42:53 +03:00
kPointerEventKindMouse ,
2023-08-06 09:00:48 +03:00
x ,
y ,
isMove ,
type ,
onExit: onExit ,
buttons: evt [ ' buttons ' ] ,
) ;
if ( pos = = null ) {
return ;
}
2023-08-09 18:42:53 +03:00
if ( type ! = ' ' ) {
evt [ ' x ' ] = 0 ;
evt [ ' y ' ] = 0 ;
} else {
evt [ ' x ' ] = ' ${ pos . x } ' ;
evt [ ' y ' ] = ' ${ pos . y } ' ;
}
2023-08-06 09:00:48 +03:00
Map < int , String > mapButtons = {
kPrimaryMouseButton: ' left ' ,
kSecondaryMouseButton: ' right ' ,
kMiddleMouseButton: ' wheel ' ,
kBackMouseButton: ' back ' ,
kForwardMouseButton: ' forward '
} ;
evt [ ' buttons ' ] = mapButtons [ evt [ ' buttons ' ] ] ? ? ' ' ;
2023-08-08 18:53:53 +03:00
bind . sessionSendMouse ( sessionId: sessionId , msg: json . encode ( modify ( evt ) ) ) ;
2023-08-06 09:00:48 +03:00
}
Point ? handlePointerDevicePos (
2023-08-09 18:42:53 +03:00
String kind ,
2023-08-06 09:00:48 +03:00
double x ,
double y ,
bool isMove ,
String evtType , {
bool onExit = false ,
int buttons = kPrimaryMouseButton ,
} ) {
2023-02-28 10:30:46 +03:00
y - = CanvasModel . topToEdge ;
x - = CanvasModel . leftToEdge ;
2022-09-27 15:35:02 +03:00
final canvasModel = parent . target ! . canvasModel ;
final ffiModel = parent . target ! . ffiModel ;
if ( isMove ) {
canvasModel . moveDesktopMouse ( x , y ) ;
}
2023-08-06 09:00:48 +03:00
final nearThr = 3 ;
var nearRight = ( canvasModel . size . width - x ) < nearThr ;
var nearBottom = ( canvasModel . size . height - y ) < nearThr ;
2022-09-27 15:35:02 +03:00
final d = ffiModel . display ;
2023-02-24 18:38:23 +03:00
final imageWidth = d . width * canvasModel . scale ;
final imageHeight = d . height * canvasModel . scale ;
2022-09-27 15:35:02 +03:00
if ( canvasModel . scrollStyle = = ScrollStyle . scrollbar ) {
x + = imageWidth * canvasModel . scrollX ;
y + = imageHeight * canvasModel . scrollY ;
// boxed size is a center widget
if ( canvasModel . size . width > imageWidth ) {
x - = ( ( canvasModel . size . width - imageWidth ) / 2 ) ;
}
if ( canvasModel . size . height > imageHeight ) {
y - = ( ( canvasModel . size . height - imageHeight ) / 2 ) ;
}
} else {
x - = canvasModel . x ;
y - = canvasModel . y ;
}
x / = canvasModel . scale ;
y / = canvasModel . scale ;
2023-02-24 18:38:23 +03:00
if ( canvasModel . scale > 0 & & canvasModel . scale < 1 ) {
final step = 1.0 / canvasModel . scale - 1 ;
if ( nearRight ) {
x + = step ;
}
if ( nearBottom ) {
y + = step ;
}
}
2022-09-27 15:35:02 +03:00
x + = d . x ;
y + = d . y ;
2023-03-13 16:03:43 +03:00
if ( onExit ) {
final pos = setNearestEdge ( x , y , d ) ;
x = pos . dx ;
y = pos . dy ;
}
2023-02-26 18:29:36 +03:00
var evtX = 0 ;
var evtY = 0 ;
try {
2023-02-26 18:31:11 +03:00
evtX = x . round ( ) ;
evtY = y . round ( ) ;
2023-02-26 18:29:36 +03:00
} catch ( e ) {
debugPrintStack (
label: ' canvasModel.scale value ${ canvasModel . scale } , $ e ' ) ;
2023-08-06 09:00:48 +03:00
return null ;
2023-02-26 18:29:36 +03:00
}
2023-02-10 16:32:51 +03:00
2023-03-13 16:03:43 +03:00
int minX = d . x . toInt ( ) ;
int maxX = ( d . x + d . width ) . toInt ( ) - 1 ;
int minY = d . y . toInt ( ) ;
int maxY = ( d . y + d . height ) . toInt ( ) - 1 ;
2023-03-13 16:18:10 +03:00
evtX = trySetNearestRange ( evtX , minX , maxX , 5 ) ;
evtY = trySetNearestRange ( evtY , minY , maxY , 5 ) ;
2023-08-09 18:42:53 +03:00
if ( kind = = kPointerEventKindMouse ) {
if ( evtX < minX | | evtY < minY | | evtX > maxX | | evtY > maxY ) {
// If left mouse up, no early return.
if ( ! ( buttons = = kPrimaryMouseButton & & evtType = = ' up ' ) ) {
return null ;
}
2023-02-10 16:32:51 +03:00
}
}
2023-08-06 09:00:48 +03:00
return Point ( evtX , evtY ) ;
2022-09-27 15:35:02 +03:00
}
/// Web only
void listenToMouse ( bool yesOrNo ) {
if ( yesOrNo ) {
platformFFI . startDesktopWebListener ( ) ;
} else {
platformFFI . stopDesktopWebListener ( ) ;
2022-09-15 09:49:59 +03:00
}
}
}