refactor
This commit is contained in:
parent
13eee42008
commit
b594c8836e
@ -1,19 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:ffi/ffi.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:ffi';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
|
||||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||||
import 'dart:typed_data';
|
import 'model.dart';
|
||||||
import 'dart:ui' as ui;
|
|
||||||
|
|
||||||
class RgbaFrame extends Struct {
|
|
||||||
@Uint32()
|
|
||||||
int len;
|
|
||||||
Pointer<Uint8> data;
|
|
||||||
}
|
|
||||||
|
|
||||||
class HexColor extends Color {
|
class HexColor extends Color {
|
||||||
HexColor(final String hexColor) : super(_getColorFromHex(hexColor));
|
HexColor(final String hexColor) : super(_getColorFromHex(hexColor));
|
||||||
@ -33,199 +21,6 @@ class MyTheme {
|
|||||||
static const Color accent = Color(0xFF0071FF);
|
static const Color accent = Color(0xFF0071FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef F1 = void Function(Pointer<Utf8>);
|
|
||||||
typedef F2 = Pointer<Utf8> Function(Pointer<Utf8>, Pointer<Utf8>);
|
|
||||||
typedef F3 = void Function(Pointer<Utf8>, Pointer<Utf8>);
|
|
||||||
typedef F4 = void Function(Pointer<RgbaFrame>);
|
|
||||||
typedef F5 = Pointer<RgbaFrame> Function();
|
|
||||||
|
|
||||||
// https://juejin.im/post/6844903864852807694
|
|
||||||
class FfiModel with ChangeNotifier {
|
|
||||||
FfiModel() {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<Null> init() async {
|
|
||||||
await FFI.init();
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ImageModel with ChangeNotifier {
|
|
||||||
ui.Image _image;
|
|
||||||
|
|
||||||
ui.Image get image => _image;
|
|
||||||
|
|
||||||
void update(ui.Image image) {
|
|
||||||
_image = image;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CursorModel with ChangeNotifier {
|
|
||||||
ui.Image _image;
|
|
||||||
final _images = Map<int, ui.Image>();
|
|
||||||
double _x = 0;
|
|
||||||
double _y = 0;
|
|
||||||
double _hotx = 0;
|
|
||||||
double _hoty = 0;
|
|
||||||
|
|
||||||
ui.Image get image => _image;
|
|
||||||
double get x => _x;
|
|
||||||
double get y => _y;
|
|
||||||
|
|
||||||
void updateCursorData(Map<String, dynamic> evt) {
|
|
||||||
var id = int.parse(evt['id']);
|
|
||||||
_hotx = double.parse(evt['hotx']);
|
|
||||||
_hoty = double.parse(evt['hoty']);
|
|
||||||
var width = int.parse(evt['width']);
|
|
||||||
var height = int.parse(evt['height']);
|
|
||||||
List<dynamic> colors = json.decode(evt['colors']);
|
|
||||||
final rgba = Uint8List.fromList(colors.map((s) => s as int).toList());
|
|
||||||
ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888,
|
|
||||||
(image) {
|
|
||||||
_image = image;
|
|
||||||
_images[id] = image;
|
|
||||||
notifyListeners();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateCursorId(Map<String, dynamic> evt) {
|
|
||||||
final tmp = _images[int.parse(evt['id'])];
|
|
||||||
if (tmp != null) {
|
|
||||||
_image = tmp;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateCursorPosition(Map<String, dynamic> evt) {
|
|
||||||
_x = double.parse(evt['x']);
|
|
||||||
_y = double.parse(evt['y']);
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
_image = null;
|
|
||||||
_images.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FFI {
|
|
||||||
static F1 _freeCString;
|
|
||||||
static F2 _getByName;
|
|
||||||
static F3 _setByName;
|
|
||||||
static F4 _freeRgba;
|
|
||||||
static F5 _getRgba;
|
|
||||||
static Pointer<RgbaFrame> _lastRgbaFrame;
|
|
||||||
static final imageModel = ImageModel();
|
|
||||||
static final ffiModel = FfiModel();
|
|
||||||
static final cursorModel = CursorModel();
|
|
||||||
|
|
||||||
static String getId() {
|
|
||||||
return getByName('remote_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<Peer> peers() {
|
|
||||||
try {
|
|
||||||
List<dynamic> peers = json.decode(getByName('peers'));
|
|
||||||
return peers
|
|
||||||
.map((s) => s as List<dynamic>)
|
|
||||||
.map((s) =>
|
|
||||||
Peer.fromJson(s[0] as String, s[1] as Map<String, dynamic>))
|
|
||||||
.toList();
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void connect(String id) {
|
|
||||||
setByName('connect', id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void clearRgbaFrame() {
|
|
||||||
if (_lastRgbaFrame != null && _lastRgbaFrame != nullptr)
|
|
||||||
_freeRgba(_lastRgbaFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8List getRgba() {
|
|
||||||
_lastRgbaFrame = _getRgba();
|
|
||||||
if (_lastRgbaFrame == null || _lastRgbaFrame == nullptr) return null;
|
|
||||||
final ref = _lastRgbaFrame.ref;
|
|
||||||
return Uint8List.sublistView(ref.data.asTypedList(ref.len));
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, dynamic> popEvent() {
|
|
||||||
var s = getByName('event');
|
|
||||||
if (s == '') return null;
|
|
||||||
try {
|
|
||||||
Map<String, dynamic> event = json.decode(s);
|
|
||||||
return event;
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void login(String password, bool remember) {
|
|
||||||
setByName(
|
|
||||||
'login',
|
|
||||||
json.encode({
|
|
||||||
'password': password,
|
|
||||||
'remember': remember ? 'true' : 'false',
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void close() {
|
|
||||||
setByName('close', '');
|
|
||||||
FFI.imageModel.update(null);
|
|
||||||
FFI.cursorModel.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setByName(String name, String value) {
|
|
||||||
_setByName(Utf8.toUtf8(name), Utf8.toUtf8(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
static String getByName(String name, {String arg = ''}) {
|
|
||||||
var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg));
|
|
||||||
assert(p != nullptr && p != null);
|
|
||||||
var res = Utf8.fromUtf8(p);
|
|
||||||
// https://github.com/brickpop/flutter-rust-ffi
|
|
||||||
_freeCString(p);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<Null> init() async {
|
|
||||||
final dylib = Platform.isAndroid
|
|
||||||
? DynamicLibrary.open('librustdesk.so')
|
|
||||||
: DynamicLibrary.process();
|
|
||||||
_getByName = dylib.lookupFunction<F2, F2>('get_by_name');
|
|
||||||
_setByName =
|
|
||||||
dylib.lookupFunction<Void Function(Pointer<Utf8>, Pointer<Utf8>), F3>(
|
|
||||||
'set_by_name');
|
|
||||||
_freeCString = dylib
|
|
||||||
.lookupFunction<Void Function(Pointer<Utf8>), F1>('rust_cstr_free');
|
|
||||||
_freeRgba = dylib
|
|
||||||
.lookupFunction<Void Function(Pointer<RgbaFrame>), F4>('free_rgba');
|
|
||||||
_getRgba = dylib.lookupFunction<F5, F5>('get_rgba');
|
|
||||||
final dir = (await getApplicationDocumentsDirectory()).path;
|
|
||||||
setByName('init', dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Peer {
|
|
||||||
final String id;
|
|
||||||
final String username;
|
|
||||||
final String hostname;
|
|
||||||
final String platform;
|
|
||||||
|
|
||||||
Peer.fromJson(String id, Map<String, dynamic> json)
|
|
||||||
: id = id,
|
|
||||||
username = json['username'],
|
|
||||||
hostname = json['hostname'],
|
|
||||||
platform = json['platform'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/huangjianke/flutter_easyloading
|
// https://github.com/huangjianke/flutter_easyloading
|
||||||
void showLoading(String text) {
|
void showLoading(String text) {
|
||||||
dismissLoading();
|
dismissLoading();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'common.dart';
|
import 'common.dart';
|
||||||
|
import 'model.dart';
|
||||||
import 'remote_page.dart';
|
import 'remote_page.dart';
|
||||||
|
|
||||||
class HomePage extends StatefulWidget {
|
class HomePage extends StatefulWidget {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'common.dart';
|
import 'model.dart';
|
||||||
import 'home_page.dart';
|
import 'home_page.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
327
flutter_hbb/lib/model.dart
Normal file
327
flutter_hbb/lib/model.dart
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
import 'package:ffi/ffi.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:ffi';
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'dart:async';
|
||||||
|
import 'common.dart';
|
||||||
|
|
||||||
|
class RgbaFrame extends Struct {
|
||||||
|
@Uint32()
|
||||||
|
int len;
|
||||||
|
Pointer<Uint8> data;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef F1 = void Function(Pointer<Utf8>);
|
||||||
|
typedef F2 = Pointer<Utf8> Function(Pointer<Utf8>, Pointer<Utf8>);
|
||||||
|
typedef F3 = void Function(Pointer<Utf8>, Pointer<Utf8>);
|
||||||
|
typedef F4 = void Function(Pointer<RgbaFrame>);
|
||||||
|
typedef F5 = Pointer<RgbaFrame> Function();
|
||||||
|
|
||||||
|
// https://juejin.im/post/6844903864852807694
|
||||||
|
class FfiModel with ChangeNotifier {
|
||||||
|
PeerInfo _pi = PeerInfo();
|
||||||
|
Display _display = Display();
|
||||||
|
bool _decoding = false;
|
||||||
|
|
||||||
|
FfiModel() {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Null> init() async {
|
||||||
|
await FFI.init();
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
_decoding = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(String id, BuildContext context) {
|
||||||
|
var evt = FFI.popEvent();
|
||||||
|
if (evt != null) {
|
||||||
|
var name = evt['name'];
|
||||||
|
if (name == 'msgbox') {
|
||||||
|
handleMsgbox(evt, id, context);
|
||||||
|
} else if (name == 'peer_info') {
|
||||||
|
handlePeerInfo(evt);
|
||||||
|
} else if (name == 'switch_display') {
|
||||||
|
handleSwitchDisplay(evt);
|
||||||
|
} else if (name == 'cursor_data') {
|
||||||
|
FFI.cursorModel.updateCursorData(evt);
|
||||||
|
} else if (name == 'cursor_id') {
|
||||||
|
FFI.cursorModel.updateCursorId(evt);
|
||||||
|
} else if (name == 'cursor_position') {
|
||||||
|
FFI.cursorModel.updateCursorPosition(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_decoding) {
|
||||||
|
var rgba = FFI.getRgba();
|
||||||
|
if (rgba != null) {
|
||||||
|
_decoding = true;
|
||||||
|
ui.decodeImageFromPixels(
|
||||||
|
rgba, _display.width, _display.height, ui.PixelFormat.bgra8888,
|
||||||
|
(image) {
|
||||||
|
FFI.clearRgbaFrame();
|
||||||
|
_decoding = false;
|
||||||
|
try {
|
||||||
|
// my throw exception, because the listener maybe already dispose
|
||||||
|
FFI.imageModel.update(image);
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleMsgbox(Map<String, dynamic> evt, String id, BuildContext context) {
|
||||||
|
var type = evt['type'];
|
||||||
|
var title = evt['title'];
|
||||||
|
var text = evt['text'];
|
||||||
|
if (type == 're-input-password') {
|
||||||
|
wrongPasswordDialog(id, context);
|
||||||
|
} else if (type == 'input-password') {
|
||||||
|
enterPasswordDialog(id, context);
|
||||||
|
} else {
|
||||||
|
msgbox(type, title, text, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleSwitchDisplay(Map<String, dynamic> evt) {
|
||||||
|
_pi.currentDisplay = int.parse(evt['display']);
|
||||||
|
_display.x = double.parse(evt['x']);
|
||||||
|
_display.y = double.parse(evt['y']);
|
||||||
|
_display.width = int.parse(evt['width']);
|
||||||
|
_display.height = int.parse(evt['height']);
|
||||||
|
FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handlePeerInfo(Map<String, dynamic> evt) {
|
||||||
|
dismissLoading();
|
||||||
|
_pi.username = evt['username'];
|
||||||
|
_pi.hostname = evt['hostname'];
|
||||||
|
_pi.platform = evt['platform'];
|
||||||
|
_pi.sasEnabled = evt['sas_enabled'] == "true";
|
||||||
|
_pi.currentDisplay = int.parse(evt['current_display']);
|
||||||
|
List<dynamic> displays = json.decode(evt['displays']);
|
||||||
|
_pi.displays = List<Display>();
|
||||||
|
for (int i = 0; i < displays.length; ++i) {
|
||||||
|
Map<String, dynamic> d0 = displays[i];
|
||||||
|
var d = Display();
|
||||||
|
d.x = d0['x'].toDouble();
|
||||||
|
d.y = d0['y'].toDouble();
|
||||||
|
d.width = d0['width'];
|
||||||
|
d.height = d0['height'];
|
||||||
|
_pi.displays.add(d);
|
||||||
|
}
|
||||||
|
if (_pi.currentDisplay < _pi.displays.length) {
|
||||||
|
_display = _pi.displays[_pi.currentDisplay];
|
||||||
|
FFI.cursorModel.updateDisplayOrigin(_display.x, _display.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImageModel with ChangeNotifier {
|
||||||
|
ui.Image _image;
|
||||||
|
|
||||||
|
ui.Image get image => _image;
|
||||||
|
|
||||||
|
void update(ui.Image image) {
|
||||||
|
_image = image;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CursorModel with ChangeNotifier {
|
||||||
|
ui.Image _image;
|
||||||
|
final _images = Map<int, ui.Image>();
|
||||||
|
double _x = 0;
|
||||||
|
double _y = 0;
|
||||||
|
double _hotx = 0;
|
||||||
|
double _hoty = 0;
|
||||||
|
double _displayOriginX = 0;
|
||||||
|
double _displayOriginY = 0;
|
||||||
|
|
||||||
|
ui.Image get image => _image;
|
||||||
|
double get x => _x - _displayOriginX - _hotx;
|
||||||
|
double get y => _y - _displayOriginY - _hoty;
|
||||||
|
|
||||||
|
void updateCursorData(Map<String, dynamic> evt) {
|
||||||
|
var id = int.parse(evt['id']);
|
||||||
|
_hotx = double.parse(evt['hotx']);
|
||||||
|
_hoty = double.parse(evt['hoty']);
|
||||||
|
var width = int.parse(evt['width']);
|
||||||
|
var height = int.parse(evt['height']);
|
||||||
|
List<dynamic> colors = json.decode(evt['colors']);
|
||||||
|
final rgba = Uint8List.fromList(colors.map((s) => s as int).toList());
|
||||||
|
ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888,
|
||||||
|
(image) {
|
||||||
|
_image = image;
|
||||||
|
_images[id] = image;
|
||||||
|
try {
|
||||||
|
// my throw exception, because the listener maybe already dispose
|
||||||
|
notifyListeners();
|
||||||
|
} catch (e) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateCursorId(Map<String, dynamic> evt) {
|
||||||
|
final tmp = _images[int.parse(evt['id'])];
|
||||||
|
if (tmp != null) {
|
||||||
|
_image = tmp;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateCursorPosition(Map<String, dynamic> evt) {
|
||||||
|
_x = double.parse(evt['x']);
|
||||||
|
_y = double.parse(evt['y']);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateDisplayOrigin(double x, double y) {
|
||||||
|
_displayOriginX = x;
|
||||||
|
_displayOriginY = y;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
_image = null;
|
||||||
|
_images.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FFI {
|
||||||
|
static F1 _freeCString;
|
||||||
|
static F2 _getByName;
|
||||||
|
static F3 _setByName;
|
||||||
|
static F4 _freeRgba;
|
||||||
|
static F5 _getRgba;
|
||||||
|
static Pointer<RgbaFrame> _lastRgbaFrame;
|
||||||
|
static final imageModel = ImageModel();
|
||||||
|
static final ffiModel = FfiModel();
|
||||||
|
static final cursorModel = CursorModel();
|
||||||
|
|
||||||
|
static String getId() {
|
||||||
|
return getByName('remote_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<Peer> peers() {
|
||||||
|
try {
|
||||||
|
List<dynamic> peers = json.decode(getByName('peers'));
|
||||||
|
return peers
|
||||||
|
.map((s) => s as List<dynamic>)
|
||||||
|
.map((s) =>
|
||||||
|
Peer.fromJson(s[0] as String, s[1] as Map<String, dynamic>))
|
||||||
|
.toList();
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void connect(String id) {
|
||||||
|
setByName('connect', id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearRgbaFrame() {
|
||||||
|
if (_lastRgbaFrame != null && _lastRgbaFrame != nullptr)
|
||||||
|
_freeRgba(_lastRgbaFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Uint8List getRgba() {
|
||||||
|
_lastRgbaFrame = _getRgba();
|
||||||
|
if (_lastRgbaFrame == null || _lastRgbaFrame == nullptr) return null;
|
||||||
|
final ref = _lastRgbaFrame.ref;
|
||||||
|
return Uint8List.sublistView(ref.data.asTypedList(ref.len));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, dynamic> popEvent() {
|
||||||
|
var s = getByName('event');
|
||||||
|
if (s == '') return null;
|
||||||
|
try {
|
||||||
|
Map<String, dynamic> event = json.decode(s);
|
||||||
|
return event;
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void login(String password, bool remember) {
|
||||||
|
setByName(
|
||||||
|
'login',
|
||||||
|
json.encode({
|
||||||
|
'password': password,
|
||||||
|
'remember': remember ? 'true' : 'false',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close() {
|
||||||
|
setByName('close', '');
|
||||||
|
FFI.imageModel.update(null);
|
||||||
|
FFI.cursorModel.clear();
|
||||||
|
FFI.ffiModel.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setByName(String name, String value) {
|
||||||
|
_setByName(Utf8.toUtf8(name), Utf8.toUtf8(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getByName(String name, {String arg = ''}) {
|
||||||
|
var p = _getByName(Utf8.toUtf8(name), Utf8.toUtf8(arg));
|
||||||
|
assert(p != nullptr && p != null);
|
||||||
|
var res = Utf8.fromUtf8(p);
|
||||||
|
// https://github.com/brickpop/flutter-rust-ffi
|
||||||
|
_freeCString(p);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<Null> init() async {
|
||||||
|
final dylib = Platform.isAndroid
|
||||||
|
? DynamicLibrary.open('librustdesk.so')
|
||||||
|
: DynamicLibrary.process();
|
||||||
|
_getByName = dylib.lookupFunction<F2, F2>('get_by_name');
|
||||||
|
_setByName =
|
||||||
|
dylib.lookupFunction<Void Function(Pointer<Utf8>, Pointer<Utf8>), F3>(
|
||||||
|
'set_by_name');
|
||||||
|
_freeCString = dylib
|
||||||
|
.lookupFunction<Void Function(Pointer<Utf8>), F1>('rust_cstr_free');
|
||||||
|
_freeRgba = dylib
|
||||||
|
.lookupFunction<Void Function(Pointer<RgbaFrame>), F4>('free_rgba');
|
||||||
|
_getRgba = dylib.lookupFunction<F5, F5>('get_rgba');
|
||||||
|
final dir = (await getApplicationDocumentsDirectory()).path;
|
||||||
|
setByName('init', dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Peer {
|
||||||
|
final String id;
|
||||||
|
final String username;
|
||||||
|
final String hostname;
|
||||||
|
final String platform;
|
||||||
|
|
||||||
|
Peer.fromJson(String id, Map<String, dynamic> json)
|
||||||
|
: id = id,
|
||||||
|
username = json['username'],
|
||||||
|
hostname = json['hostname'],
|
||||||
|
platform = json['platform'];
|
||||||
|
}
|
||||||
|
|
||||||
|
class Display {
|
||||||
|
double x = 0;
|
||||||
|
double y = 0;
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PeerInfo {
|
||||||
|
String username;
|
||||||
|
String hostname;
|
||||||
|
String platform;
|
||||||
|
bool sasEnabled;
|
||||||
|
int currentDisplay;
|
||||||
|
List<Display> displays;
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'common.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'common.dart';
|
||||||
|
import 'model.dart';
|
||||||
|
|
||||||
class RemotePage extends StatefulWidget {
|
class RemotePage extends StatefulWidget {
|
||||||
RemotePage({Key key, this.id}) : super(key: key);
|
RemotePage({Key key, this.id}) : super(key: key);
|
||||||
@ -19,9 +19,6 @@ class RemotePage extends StatefulWidget {
|
|||||||
// https://github.com/hanxu317317/flutter_plan_demo/blob/master/lib/src/enter.dart
|
// https://github.com/hanxu317317/flutter_plan_demo/blob/master/lib/src/enter.dart
|
||||||
class _RemotePageState extends State<RemotePage> {
|
class _RemotePageState extends State<RemotePage> {
|
||||||
Timer _interval;
|
Timer _interval;
|
||||||
PeerInfo _pi = PeerInfo();
|
|
||||||
Display _display = Display();
|
|
||||||
bool _decoding = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -42,88 +39,11 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
FFI.close();
|
FFI.close();
|
||||||
_interval.cancel();
|
_interval.cancel();
|
||||||
dismissLoading();
|
dismissLoading();
|
||||||
_decoding = null;
|
|
||||||
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
|
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
|
||||||
}
|
}
|
||||||
|
|
||||||
void interval() {
|
void interval() {
|
||||||
var evt = FFI.popEvent();
|
FFI.ffiModel.update(widget.id, context);
|
||||||
if (evt != null) {
|
|
||||||
var name = evt['name'];
|
|
||||||
if (name == 'msgbox') {
|
|
||||||
handleMsgbox(evt);
|
|
||||||
} else if (name == 'peer_info') {
|
|
||||||
handlePeerInfo(evt);
|
|
||||||
} else if (name == 'switch_display') {
|
|
||||||
handleSwitchDisplay(evt);
|
|
||||||
} else if (name == 'cursor_data') {
|
|
||||||
FFI.cursorModel.updateCursorData(evt);
|
|
||||||
} else if (name == 'cursor_id') {
|
|
||||||
FFI.cursorModel.updateCursorId(evt);
|
|
||||||
} else if (name == 'cursor_position') {
|
|
||||||
FFI.cursorModel.updateCursorPosition(evt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!_decoding) {
|
|
||||||
var rgba = FFI.getRgba();
|
|
||||||
if (rgba != null) {
|
|
||||||
_decoding = true;
|
|
||||||
ui.decodeImageFromPixels(
|
|
||||||
rgba, _display.width, _display.height, ui.PixelFormat.bgra8888,
|
|
||||||
(image) {
|
|
||||||
FFI.clearRgbaFrame();
|
|
||||||
if (_decoding == null) return;
|
|
||||||
_decoding = false;
|
|
||||||
FFI.imageModel.update(image);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleSwitchDisplay(Map<String, dynamic> evt) {
|
|
||||||
_pi.currentDisplay = int.parse(evt['display']);
|
|
||||||
_display.x = int.parse(evt['x']);
|
|
||||||
_display.y = int.parse(evt['y']);
|
|
||||||
_display.width = int.parse(evt['width']);
|
|
||||||
_display.height = int.parse(evt['height']);
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
void handlePeerInfo(Map<String, dynamic> evt) {
|
|
||||||
dismissLoading();
|
|
||||||
_pi.username = evt['username'];
|
|
||||||
_pi.hostname = evt['hostname'];
|
|
||||||
_pi.platform = evt['platform'];
|
|
||||||
_pi.sasEnabled = evt['sas_enabled'] == "true";
|
|
||||||
_pi.currentDisplay = int.parse(evt['current_display']);
|
|
||||||
List<dynamic> displays = json.decode(evt['displays']);
|
|
||||||
_pi.displays = List<Display>();
|
|
||||||
for (int i = 0; i < displays.length; ++i) {
|
|
||||||
Map<String, dynamic> d0 = displays[i];
|
|
||||||
var d = Display();
|
|
||||||
d.x = d0['x'];
|
|
||||||
d.y = d0['y'];
|
|
||||||
d.width = d0['width'];
|
|
||||||
d.height = d0['height'];
|
|
||||||
_pi.displays.add(d);
|
|
||||||
}
|
|
||||||
if (_pi.currentDisplay < _pi.displays.length) {
|
|
||||||
_display = _pi.displays[_pi.currentDisplay];
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleMsgbox(Map<String, dynamic> evt) {
|
|
||||||
var type = evt['type'];
|
|
||||||
var title = evt['title'];
|
|
||||||
var text = evt['text'];
|
|
||||||
if (type == 're-input-password') {
|
|
||||||
wrongPasswordDialog(widget.id, context);
|
|
||||||
} else if (type == 'input-password') {
|
|
||||||
enterPasswordDialog(widget.id, context);
|
|
||||||
} else {
|
|
||||||
msgbox(type, title, text, context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -170,19 +90,3 @@ class ImagePainter extends CustomPainter {
|
|||||||
return oldDelegate != this;
|
return oldDelegate != this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Display {
|
|
||||||
int x = 0;
|
|
||||||
int y = 0;
|
|
||||||
int width = 0;
|
|
||||||
int height = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
class PeerInfo {
|
|
||||||
String username;
|
|
||||||
String hostname;
|
|
||||||
String platform;
|
|
||||||
bool sasEnabled;
|
|
||||||
int currentDisplay;
|
|
||||||
List<Display> displays;
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user