2022-11-20 17:46:27 +03:00
import ' dart:async ' ;
2022-09-27 15:35:02 +03:00
import ' dart:convert ' ;
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
}
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
var trackpadScrollDistance = Offset . zero ;
Timer ? _flingTimer ;
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
get id = > parent . target ? . id ? ? " " ;
InputModel ( this . parent ) ;
2022-09-15 09:49:59 +03:00
KeyEventResult handleRawKeyEvent ( FocusNode data , RawKeyEvent e ) {
2022-11-16 10:09:29 +03:00
bind . sessionGetKeyboardMode ( id: id ) . then ( ( result ) {
2022-09-15 09:49:59 +03:00
keyboardMode = result . toString ( ) ;
} ) ;
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
}
}
2022-09-15 09:49:59 +03:00
if ( keyboardMode = = ' map ' ) {
mapKeyboardMode ( e ) ;
} else if ( keyboardMode = = ' translate ' ) {
legacyKeyboardMode ( e ) ;
} else {
legacyKeyboardMode ( e ) ;
}
return KeyEventResult . handled ;
}
void mapKeyboardMode ( RawKeyEvent e ) {
int scanCode ;
int keyCode ;
bool down ;
if ( e . data is RawKeyEventDataMacOs ) {
RawKeyEventDataMacOs newData = e . data as RawKeyEventDataMacOs ;
scanCode = newData . keyCode ;
keyCode = newData . keyCode ;
} else if ( e . data is RawKeyEventDataWindows ) {
RawKeyEventDataWindows newData = e . data as RawKeyEventDataWindows ;
scanCode = newData . scanCode ;
keyCode = newData . keyCode ;
} 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
scanCode = 0 ;
keyCode = newData . scanCode ;
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 ;
scanCode = newData . scanCode + 8 ;
keyCode = newData . keyCode ;
2022-09-29 13:56:47 +03:00
} else {
2022-09-15 09:49:59 +03:00
scanCode = - 1 ;
keyCode = - 1 ;
}
if ( e is RawKeyDownEvent ) {
down = true ;
} else {
down = false ;
}
2023-01-10 09:11:49 +03:00
inputRawKey ( e . character ? ? ' ' , keyCode , scanCode , 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
void inputRawKey ( String name , int keyCode , int scanCode , 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 (
id: id ,
name: name ,
keycode: keyCode ,
scancode: scanCode ,
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 } ) {
if ( ! parent . target ! . ffiModel . keyboard ( ) ) return ;
bind . sessionInputKey (
id: id ,
name: name ,
down: down ? ? false ,
press: press ? ? true ,
alt: alt ,
ctrl: ctrl ,
shift: shift ,
command: command ) ;
2022-09-15 09:49:59 +03:00
}
Map < String , dynamic > getEvent ( PointerEvent evt , String type ) {
final Map < String , dynamic > out = { } ;
out [ ' x ' ] = evt . position . dx ;
out [ ' y ' ] = evt . position . dy ;
2022-09-27 15:35:02 +03:00
if ( alt ) out [ ' alt ' ] = ' true ' ;
if ( shift ) out [ ' shift ' ] = ' true ' ;
if ( ctrl ) out [ ' ctrl ' ] = ' true ' ;
if ( command ) out [ ' command ' ] = ' true ' ;
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 ) ;
}
/// Send scroll event with scroll distance [y].
void scroll ( int y ) {
bind . sessionSendMouse (
id: id ,
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.
Map < String , String > modify ( Map < String , String > evt ) {
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 ) {
if ( ! parent . target ! . ffiModel . keyboard ( ) ) return ;
bind . sessionSendMouse (
id: id ,
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 ( ) ;
2022-09-27 15:35:02 +03:00
bind . sessionEnterOrLeave ( id: id , enter: enter ) ;
}
/// Send mouse movement event with distance in [x] and [y].
void moveMouse ( double x , double y ) {
if ( ! parent . target ! . ffiModel . keyboard ( ) ) return ;
var x2 = x . toInt ( ) ;
var y2 = y . toInt ( ) ;
bind . sessionSendMouse (
id: id , msg: json . encode ( modify ( { ' x ' : ' $ x2 ' , ' y ' : ' $ y2 ' } ) ) ) ;
}
2022-09-15 09:49:59 +03:00
void onPointHoverImage ( PointerHoverEvent e ) {
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-01-04 15:06:48 +03:00
handleMouse ( getEvent ( e , _kMouseEventMove ) ) ;
2022-09-15 09:49:59 +03:00
}
}
2022-11-21 11:29:29 +03:00
/ *
2022-11-20 17:46:27 +03:00
int _signOrZero ( num x ) {
if ( x = = 0 ) {
return 0 ;
} else {
return x > 0 ? 1 : - 1 ;
}
}
void onPointerPanZoomStart ( PointerPanZoomStartEvent e ) { }
// https://docs.flutter.dev/release/breaking-changes/trackpad-gestures
// TODO(support zoom in/out)
void onPointerPanZoomUpdate ( PointerPanZoomUpdateEvent e ) {
var delta = e . panDelta ;
trackpadScrollDistance + = delta ;
bind . sessionSendMouse (
id: id ,
msg:
' {"type": "trackpad", "x": " ${ delta . dx . toInt ( ) } ", "y": " ${ delta . dy . toInt ( ) } "} ' ) ;
}
// Simple simulation for fling.
void _scheduleFling ( var x , y , dx , dy ) {
if ( dx < = 0 & & dy < = 0 ) {
return ;
}
_flingTimer = Timer ( Duration ( milliseconds: 10 ) , ( ) {
bind . sessionSendMouse (
id: id , msg: ' {"type": "trackpad", "x": " $ x ", "y": " $ y "} ' ) ;
dx - - ;
dy - - ;
if ( dx = = 0 ) {
x = 0 ;
}
if ( dy = = 0 ) {
y = 0 ;
}
_scheduleFling ( x , y , dx , dy ) ;
} ) ;
}
void onPointerPanZoomEnd ( PointerPanZoomEndEvent e ) {
var x = _signOrZero ( trackpadScrollDistance . dx ) ;
var y = _signOrZero ( trackpadScrollDistance . dy ) ;
var dx = trackpadScrollDistance . dx . abs ( ) ~ / 40 ;
var dy = trackpadScrollDistance . dy . abs ( ) ~ / 40 ;
_scheduleFling ( x , y , dx , dy ) ;
trackpadScrollDistance = Offset . zero ;
}
2022-11-21 11:29:29 +03:00
* /
2022-11-20 17:46:27 +03:00
2022-09-15 09:49:59 +03:00
void onPointDownImage ( PointerDownEvent e ) {
2022-09-27 15:35:02 +03:00
debugPrint ( " onPointDownImage " ) ;
2022-09-15 09:49:59 +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-01-04 15:06:48 +03:00
handleMouse ( getEvent ( e , _kMouseEventDown ) ) ;
2022-09-15 09:49:59 +03:00
}
}
void onPointUpImage ( PointerUpEvent e ) {
if ( e . kind ! = ui . PointerDeviceKind . mouse ) return ;
2022-09-27 17:16:27 +03:00
if ( isPhysicalMouse . value ) {
2023-01-04 15:06:48 +03:00
handleMouse ( getEvent ( e , _kMouseEventUp ) ) ;
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-01-04 15:06:48 +03:00
handleMouse ( getEvent ( e , _kMouseEventMove ) ) ;
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 (
2022-09-27 15:35:02 +03:00
id: id , msg: ' {"type": "wheel", "x": " $ dx ", "y": " $ dy "} ' ) ;
}
}
void handleMouse ( Map < String , dynamic > evt ) {
2022-11-08 07:01:51 +03:00
double x = evt [ ' x ' ] ;
double y = max ( 0.0 , evt [ ' y ' ] ) ;
final cursorModel = parent . target ! . cursorModel ;
2022-11-14 10:05:44 +03:00
if ( cursorModel . isPeerControlProtected ) {
lastMousePos = ui . Offset ( x , y ) ;
2022-11-08 08:37:08 +03:00
return ;
}
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 ) ;
2022-11-08 07:01:51 +03:00
return ;
}
}
2022-11-14 10:05:44 +03:00
lastMousePos = ui . Offset ( x , y ) ;
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 ;
if ( isDesktop ) {
2022-11-01 12:01:43 +03:00
y = y - stateGlobal . tabBarHeight ;
2022-09-27 15:35:02 +03:00
}
final canvasModel = parent . target ! . canvasModel ;
final ffiModel = parent . target ! . ffiModel ;
if ( isMove ) {
canvasModel . moveDesktopMouse ( x , y ) ;
}
final d = ffiModel . display ;
if ( canvasModel . scrollStyle = = ScrollStyle . scrollbar ) {
final imageWidth = d . width * canvasModel . scale ;
final imageHeight = d . height * canvasModel . scale ;
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 ;
x + = d . x ;
y + = d . y ;
if ( type ! = ' ' ) {
x = 0 ;
y = 0 ;
}
evt [ ' x ' ] = ' ${ x . round ( ) } ' ;
evt [ ' y ' ] = ' ${ y . round ( ) } ' ;
var buttons = ' ' ;
switch ( evt [ ' buttons ' ] ) {
2023-01-05 12:14:44 +03:00
case kPrimaryMouseButton:
2022-09-27 15:35:02 +03:00
buttons = ' left ' ;
break ;
2023-01-05 12:14:44 +03:00
case kSecondaryMouseButton:
2022-09-27 15:35:02 +03:00
buttons = ' right ' ;
break ;
2023-01-05 12:14:44 +03:00
case kMiddleMouseButton:
2022-09-27 15:35:02 +03:00
buttons = ' wheel ' ;
break ;
2023-01-05 12:14:44 +03:00
case kBackMouseButton:
buttons = ' back ' ;
break ;
case kForwardMouseButton:
buttons = ' forward ' ;
break ;
2022-09-27 15:35:02 +03:00
}
evt [ ' buttons ' ] = buttons ;
bind . sessionSendMouse ( id: id , msg: json . encode ( evt ) ) ;
}
/// Web only
void listenToMouse ( bool yesOrNo ) {
if ( yesOrNo ) {
platformFFI . startDesktopWebListener ( ) ;
} else {
platformFFI . stopDesktopWebListener ( ) ;
2022-09-15 09:49:59 +03:00
}
}
}