mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-12-02 18:59:24 +08:00
Merge pull request #2276 from fufesou/fix_cursor_win_forbidden
fix predefined win forbidden cursor
This commit is contained in:
commit
5d24bc93c6
@ -356,9 +356,8 @@ class _ImagePaintState extends State<ImagePaint> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseCursor _buildCustomCursor(BuildContext context, double scale) {
|
MouseCursor _buildCursorOfCache(
|
||||||
final cursor = Provider.of<CursorModel>(context);
|
CursorModel cursor, double scale, CursorData? cache) {
|
||||||
final cache = cursor.cache ?? cursor.defaultCache;
|
|
||||||
if (cache == null) {
|
if (cache == null) {
|
||||||
return MouseCursor.defer;
|
return MouseCursor.defer;
|
||||||
} else {
|
} else {
|
||||||
@ -375,26 +374,16 @@ class _ImagePaintState extends State<ImagePaint> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseCursor _buildCustomCursor(BuildContext context, double scale) {
|
||||||
|
final cursor = Provider.of<CursorModel>(context);
|
||||||
|
final cache = cursor.cache ?? preDefaultCursor.cache;
|
||||||
|
return _buildCursorOfCache(cursor, scale, cache);
|
||||||
|
}
|
||||||
|
|
||||||
MouseCursor _buildDisabledCursor(BuildContext context, double scale) {
|
MouseCursor _buildDisabledCursor(BuildContext context, double scale) {
|
||||||
final cursor = Provider.of<CursorModel>(context);
|
final cursor = Provider.of<CursorModel>(context);
|
||||||
final cache = cursor.cache;
|
final cache = preForbiddenCursor.cache;
|
||||||
if (cache == null) {
|
return _buildCursorOfCache(cursor, scale, cache);
|
||||||
return MouseCursor.defer;
|
|
||||||
} else {
|
|
||||||
if (cursor.cachedForbidmemoryCursorData == null) {
|
|
||||||
cursor.updateForbiddenCursorBuffer();
|
|
||||||
}
|
|
||||||
final key = 'disabled_cursor_key';
|
|
||||||
cursor.addKey(key);
|
|
||||||
return FlutterCustomMemoryImageCursor(
|
|
||||||
pixbuf: cursor.cachedForbidmemoryCursorData,
|
|
||||||
key: key,
|
|
||||||
hotx: 0,
|
|
||||||
hoty: 0,
|
|
||||||
imageWidth: 32,
|
|
||||||
imageHeight: 32,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCrossScrollbarFromLayout(
|
Widget _buildCrossScrollbarFromLayout(
|
||||||
@ -521,22 +510,22 @@ class CursorPaint extends StatelessWidget {
|
|||||||
double hotx = m.hotx;
|
double hotx = m.hotx;
|
||||||
double hoty = m.hoty;
|
double hoty = m.hoty;
|
||||||
if (m.image == null) {
|
if (m.image == null) {
|
||||||
if (m.defaultCache != null) {
|
if (preDefaultCursor.image != null) {
|
||||||
hotx = m.defaultImage!.width / 2;
|
hotx = preDefaultCursor.image!.width / 2;
|
||||||
hoty = m.defaultImage!.height / 2;
|
hoty = preDefaultCursor.image!.height / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return zoomCursor.isTrue
|
return zoomCursor.isTrue
|
||||||
? CustomPaint(
|
? CustomPaint(
|
||||||
painter: ImagePainter(
|
painter: ImagePainter(
|
||||||
image: m.image ?? m.defaultImage,
|
image: m.image ?? preDefaultCursor.image,
|
||||||
x: m.x - hotx + c.x / c.scale,
|
x: m.x - hotx + c.x / c.scale,
|
||||||
y: m.y - hoty + c.y / c.scale,
|
y: m.y - hoty + c.y / c.scale,
|
||||||
scale: c.scale),
|
scale: c.scale),
|
||||||
)
|
)
|
||||||
: CustomPaint(
|
: CustomPaint(
|
||||||
painter: ImagePainter(
|
painter: ImagePainter(
|
||||||
image: m.image ?? m.defaultImage,
|
image: m.image ?? preDefaultCursor.image,
|
||||||
x: (m.x - hotx) * c.scale + c.x,
|
x: (m.x - hotx) * c.scale + c.x,
|
||||||
y: (m.y - hoty) * c.scale + c.y,
|
y: (m.y - hoty) * c.scale + c.y,
|
||||||
scale: 1.0),
|
scale: 1.0),
|
||||||
|
@ -865,14 +865,14 @@ class CursorPaint extends StatelessWidget {
|
|||||||
double hotx = m.hotx;
|
double hotx = m.hotx;
|
||||||
double hoty = m.hoty;
|
double hoty = m.hoty;
|
||||||
if (m.image == null) {
|
if (m.image == null) {
|
||||||
if (m.defaultCache != null) {
|
if (preDefaultCursor.image != null) {
|
||||||
hotx = m.defaultImage!.width / 2;
|
hotx = preDefaultCursor.image!.width / 2;
|
||||||
hoty = m.defaultImage!.height / 2;
|
hoty = preDefaultCursor.image!.height / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return CustomPaint(
|
return CustomPaint(
|
||||||
painter: ImagePainter(
|
painter: ImagePainter(
|
||||||
image: m.image ?? m.defaultImage,
|
image: m.image ?? preDefaultCursor.image,
|
||||||
x: m.x * s - hotx * s + c.x,
|
x: m.x * s - hotx * s + c.x,
|
||||||
y: m.y * s - hoty * s + c.y - adjust,
|
y: m.y * s - hoty * s + c.y - adjust,
|
||||||
scale: 1),
|
scale: 1),
|
||||||
|
@ -763,13 +763,78 @@ class CursorData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const _forbiddenCursorPng =
|
||||||
|
'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAkZQTFRFAAAA2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4GWAwCAAAAAAAA2B4GAAAAMTExAAAAAAAA2B4G2B4G2B4GAAAAmZmZkZGRAQEBAAAA2B4G2B4G2B4G////oKCgAwMDag8D2B4G2B4G2B4Gra2tBgYGbg8D2B4G2B4Gubm5CQkJTwsCVgwC2B4GxcXFDg4OAAAAAAAA2B4G2B4Gz8/PFBQUAAAAAAAA2B4G2B4G2B4G2B4G2B4G2B4G2B4GDgIA2NjYGxsbAAAAAAAA2B4GFwMB4eHhIyMjAAAAAAAA2B4G6OjoLCwsAAAAAAAA2B4G2B4G2B4G2B4G2B4GCQEA4ODgv7+/iYmJY2NjAgICAAAA9PT0Ojo6AAAAAAAAAAAA+/v7SkpKhYWFr6+vAAAAAAAA8/PzOTk5ERER9fX1KCgoAAAAgYGBKioqAAAAAAAApqamlpaWAAAAAAAAAAAAAAAAAAAAAAAALi4u/v7+GRkZAAAAAAAAAAAAAAAAAAAAfn5+AAAAAAAAV1dXkJCQAAAAAAAAAQEBAAAAAAAAAAAA7Hz6BAAAAMJ0Uk5TAAIWEwEynNz6//fVkCAatP2fDUHs6cDD8d0mPfT5fiEskiIR584A0gejr3AZ+P4plfALf5ZiTL85a4ziD6697fzN3UYE4v/4TwrNHuT///tdRKZh///+1U/ZBv///yjb///eAVL//50Cocv//6oFBbPvpGZCbfT//7cIhv///8INM///zBEcWYSZmO7//////1P////ts/////8vBv//////gv//R/z///QQz9sevP///2waXhNO/+fc//8mev/5gAe2r90MAAAByUlEQVR4nGNggANGJmYWBpyAlY2dg5OTi5uHF6s0H78AJxRwCAphyguLgKRExcQlQLSkFLq8tAwnp6ycPNABjAqKQKNElVDllVU4OVVhVquJA81Q10BRoAkUUYbJa4Edoo0sr6PLqaePLG/AyWlohKTAmJPTBFnelAFoixmSAnNOTgsUeQZLTk4rJAXWnJw2EHlbiDyDPCenHZICe04HFrh+RydnBgYWPU5uJAWinJwucPNd3dw9GDw5Ob2QFHBzcnrD7ffx9fMPCOTkDEINhmC4+3x8Q0LDwlEDIoKTMzIKKg9SEBIdE8sZh6SAJZ6Tkx0qD1YQkpCYlIwclCng0AXLQxSEpKalZyCryATKZwkhKQjJzsnNQ1KQXwBUUVhUXBJYWgZREFJeUVmFpMKlWg+anmqgCkJq6+obkG1pLEBTENLU3NKKrIKhrb2js8u4G6Kgpze0r3/CRAZMAHbkpJDJU6ZMmTqtFbuC6TNmhsyaMnsOFlmwgrnzpsxfELJwEXZ5Bp/FS3yWLlsesmLlKuwKVk9Ys5Zh3foN0zduwq5g85atDAzbpqSGbN9RhV0FGOzctWH3lD14FOzdt3H/gQw8Cg4u2gQPAwBYDXXdIH+wqAAAAABJRU5ErkJggg==';
|
||||||
|
const _defaultCursorPng =
|
||||||
|
'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAFmSURBVFiF7dWxSlxREMbx34QFDRowYBchZSxSCWlMCOwD5FGEFHap06UI7KPsAyyEEIQFqxRaCqYTsqCJFsKkuAeRXb17wrqV918dztw55zszc2fo6Oh47MR/e3zO1/iAHWmznHKGQwx9ip/LEbCfazbsoY8j/JLOhcC6sCW9wsjEwJf483AC9nPNc1+lFRwI13d+l3rYFS799rFGxJMqARv2pBXh+72XQ7gWvklPS7TmMl9Ak/M+DqrENvxAv/guKKApuKPWl0/TROK4+LbSqzhuB+OZ3fRSeFPWY+Fkyn56Y29hfgTSpnQ+s98cvorVey66uPlNFxKwZOYLCGfCs5n9NMYVrsp6mvXSoFqpqYFDvMBkStgJJe93dZOwVXxbqUnBENulydSReqUrDhcX0PT2EXarBYS3GNXMhboinBgIl9K71kg0L3+PvyYGdVpruT2MwrF0iotiXfIwus0Dj+OOjo6Of+e7ab74RkpgAAAAAElFTkSuQmCC';
|
||||||
|
|
||||||
|
final preForbiddenCursor = PredefinedCursor(
|
||||||
|
png: _forbiddenCursorPng,
|
||||||
|
id: -2,
|
||||||
|
);
|
||||||
|
final preDefaultCursor = PredefinedCursor(
|
||||||
|
png: _defaultCursorPng,
|
||||||
|
id: -1,
|
||||||
|
hotxGetter: (double w) => w / 2,
|
||||||
|
hotyGetter: (double h) => h / 2,
|
||||||
|
);
|
||||||
|
|
||||||
|
class PredefinedCursor {
|
||||||
|
ui.Image? _image;
|
||||||
|
img2.Image? _image2;
|
||||||
|
CursorData? _cache;
|
||||||
|
String png;
|
||||||
|
int id;
|
||||||
|
double Function(double)? hotxGetter;
|
||||||
|
double Function(double)? hotyGetter;
|
||||||
|
|
||||||
|
PredefinedCursor(
|
||||||
|
{required this.png, required this.id, this.hotxGetter, this.hotyGetter}) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.Image? get image => _image;
|
||||||
|
CursorData? get cache => _cache;
|
||||||
|
|
||||||
|
init() {
|
||||||
|
_image2 = img2.decodePng(base64Decode(png));
|
||||||
|
if (_image2 != null) {
|
||||||
|
() async {
|
||||||
|
final defaultImg = _image2!;
|
||||||
|
// This function is called only one time, no need to care about the performance.
|
||||||
|
Uint8List data = defaultImg.getBytes(format: img2.Format.rgba);
|
||||||
|
_image = await img.decodeImageFromPixels(
|
||||||
|
data, defaultImg.width, defaultImg.height, ui.PixelFormat.rgba8888);
|
||||||
|
|
||||||
|
double scale = 1.0;
|
||||||
|
if (Platform.isWindows) {
|
||||||
|
data = _image2!.getBytes(format: img2.Format.bgra);
|
||||||
|
} else {
|
||||||
|
data = Uint8List.fromList(img2.encodePng(_image2!));
|
||||||
|
}
|
||||||
|
|
||||||
|
_cache = CursorData(
|
||||||
|
peerId: '',
|
||||||
|
id: id,
|
||||||
|
image: _image2?.clone(),
|
||||||
|
scale: scale,
|
||||||
|
data: data,
|
||||||
|
hotxOrigin:
|
||||||
|
hotxGetter != null ? hotxGetter!(_image2!.width.toDouble()) : 0,
|
||||||
|
hotyOrigin:
|
||||||
|
hotyGetter != null ? hotyGetter!(_image2!.height.toDouble()) : 0,
|
||||||
|
width: _image2!.width,
|
||||||
|
height: _image2!.height,
|
||||||
|
);
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CursorModel with ChangeNotifier {
|
class CursorModel with ChangeNotifier {
|
||||||
ui.Image? _image;
|
ui.Image? _image;
|
||||||
ui.Image? _defaultImage;
|
|
||||||
final _images = <int, Tuple3<ui.Image, double, double>>{};
|
final _images = <int, Tuple3<ui.Image, double, double>>{};
|
||||||
CursorData? _cache;
|
CursorData? _cache;
|
||||||
final _defaultCacheId = -1;
|
|
||||||
CursorData? _defaultCache;
|
|
||||||
final _cacheMap = <int, CursorData>{};
|
final _cacheMap = <int, CursorData>{};
|
||||||
final _cacheKeys = <String>{};
|
final _cacheKeys = <String>{};
|
||||||
double _x = -10000;
|
double _x = -10000;
|
||||||
@ -785,9 +850,7 @@ class CursorModel with ChangeNotifier {
|
|||||||
WeakReference<FFI> parent;
|
WeakReference<FFI> parent;
|
||||||
|
|
||||||
ui.Image? get image => _image;
|
ui.Image? get image => _image;
|
||||||
ui.Image? get defaultImage => _defaultImage;
|
|
||||||
CursorData? get cache => _cache;
|
CursorData? get cache => _cache;
|
||||||
CursorData? get defaultCache => _getDefaultCache();
|
|
||||||
|
|
||||||
double get x => _x - _displayOriginX;
|
double get x => _x - _displayOriginX;
|
||||||
double get y => _y - _displayOriginY;
|
double get y => _y - _displayOriginY;
|
||||||
@ -801,50 +864,11 @@ class CursorModel with ChangeNotifier {
|
|||||||
DateTime.now().difference(_lastPeerMouse).inMilliseconds <
|
DateTime.now().difference(_lastPeerMouse).inMilliseconds <
|
||||||
kMouseControlTimeoutMSec;
|
kMouseControlTimeoutMSec;
|
||||||
|
|
||||||
CursorModel(this.parent) {
|
CursorModel(this.parent);
|
||||||
_getDefaultImage();
|
|
||||||
_getDefaultCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> get cachedKeys => _cacheKeys;
|
Set<String> get cachedKeys => _cacheKeys;
|
||||||
addKey(String key) => _cacheKeys.add(key);
|
addKey(String key) => _cacheKeys.add(key);
|
||||||
|
|
||||||
Future<ui.Image?> _getDefaultImage() async {
|
|
||||||
if (_defaultImage == null) {
|
|
||||||
final defaultImg = defaultCursorImage!;
|
|
||||||
// This function is called only one time, no need to care about the performance.
|
|
||||||
Uint8List data = defaultImg.getBytes(format: img2.Format.rgba);
|
|
||||||
_defaultImage = await img.decodeImageFromPixels(
|
|
||||||
data, defaultImg.width, defaultImg.height, ui.PixelFormat.rgba8888);
|
|
||||||
}
|
|
||||||
return _defaultImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
CursorData? _getDefaultCache() {
|
|
||||||
if (_defaultCache == null) {
|
|
||||||
Uint8List data;
|
|
||||||
double scale = 1.0;
|
|
||||||
if (Platform.isWindows) {
|
|
||||||
data = defaultCursorImage!.getBytes(format: img2.Format.bgra);
|
|
||||||
} else {
|
|
||||||
data = Uint8List.fromList(img2.encodePng(defaultCursorImage!));
|
|
||||||
}
|
|
||||||
|
|
||||||
_defaultCache = CursorData(
|
|
||||||
peerId: id,
|
|
||||||
id: _defaultCacheId,
|
|
||||||
image: defaultCursorImage?.clone(),
|
|
||||||
scale: scale,
|
|
||||||
data: data,
|
|
||||||
hotxOrigin: defaultCursorImage!.width / 2,
|
|
||||||
hotyOrigin: defaultCursorImage!.height / 2,
|
|
||||||
width: defaultCursorImage!.width,
|
|
||||||
height: defaultCursorImage!.height,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return _defaultCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remote physical display coordinate
|
// remote physical display coordinate
|
||||||
Rect getVisibleRect() {
|
Rect getVisibleRect() {
|
||||||
final size = MediaQueryData.fromWindow(ui.window).size;
|
final size = MediaQueryData.fromWindow(ui.window).size;
|
||||||
@ -1085,15 +1109,6 @@ class CursorModel with ChangeNotifier {
|
|||||||
customCursorController.freeCache(k);
|
customCursorController.freeCache(k);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint8List? cachedForbidmemoryCursorData;
|
|
||||||
void updateForbiddenCursorBuffer() {
|
|
||||||
cachedForbidmemoryCursorData ??= base64Decode(
|
|
||||||
'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAkZQTFRFAAAA2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4G2B4GWAwCAAAAAAAA2B4GAAAAMTExAAAAAAAA2B4G2B4G2B4GAAAAmZmZkZGRAQEBAAAA2B4G2B4G2B4G////oKCgAwMDag8D2B4G2B4G2B4Gra2tBgYGbg8D2B4G2B4Gubm5CQkJTwsCVgwC2B4GxcXFDg4OAAAAAAAA2B4G2B4Gz8/PFBQUAAAAAAAA2B4G2B4G2B4G2B4G2B4G2B4G2B4GDgIA2NjYGxsbAAAAAAAA2B4GFwMB4eHhIyMjAAAAAAAA2B4G6OjoLCwsAAAAAAAA2B4G2B4G2B4G2B4G2B4GCQEA4ODgv7+/iYmJY2NjAgICAAAA9PT0Ojo6AAAAAAAAAAAA+/v7SkpKhYWFr6+vAAAAAAAA8/PzOTk5ERER9fX1KCgoAAAAgYGBKioqAAAAAAAApqamlpaWAAAAAAAAAAAAAAAAAAAAAAAALi4u/v7+GRkZAAAAAAAAAAAAAAAAAAAAfn5+AAAAAAAAV1dXkJCQAAAAAAAAAQEBAAAAAAAAAAAA7Hz6BAAAAMJ0Uk5TAAIWEwEynNz6//fVkCAatP2fDUHs6cDD8d0mPfT5fiEskiIR584A0gejr3AZ+P4plfALf5ZiTL85a4ziD6697fzN3UYE4v/4TwrNHuT///tdRKZh///+1U/ZBv///yjb///eAVL//50Cocv//6oFBbPvpGZCbfT//7cIhv///8INM///zBEcWYSZmO7//////1P////ts/////8vBv//////gv//R/z///QQz9sevP///2waXhNO/+fc//8mev/5gAe2r90MAAAByUlEQVR4nGNggANGJmYWBpyAlY2dg5OTi5uHF6s0H78AJxRwCAphyguLgKRExcQlQLSkFLq8tAwnp6ycPNABjAqKQKNElVDllVU4OVVhVquJA81Q10BRoAkUUYbJa4Edoo0sr6PLqaePLG/AyWlohKTAmJPTBFnelAFoixmSAnNOTgsUeQZLTk4rJAXWnJw2EHlbiDyDPCenHZICe04HFrh+RydnBgYWPU5uJAWinJwucPNd3dw9GDw5Ob2QFHBzcnrD7ffx9fMPCOTkDEINhmC4+3x8Q0LDwlEDIoKTMzIKKg9SEBIdE8sZh6SAJZ6Tkx0qD1YQkpCYlIwclCng0AXLQxSEpKalZyCryATKZwkhKQjJzsnNQ1KQXwBUUVhUXBJYWgZREFJeUVmFpMKlWg+anmqgCkJq6+obkG1pLEBTENLU3NKKrIKhrb2js8u4G6Kgpze0r3/CRAZMAHbkpJDJU6ZMmTqtFbuC6TNmhsyaMnsOFlmwgrnzpsxfELJwEXZ5Bp/FS3yWLlsesmLlKuwKVk9Ys5Zh3foN0zduwq5g85atDAzbpqSGbN9RhV0FGOzctWH3lD14FOzdt3H/gQw8Cg4u2gQPAwBYDXXdIH+wqAAAAABJRU5ErkJggg==');
|
|
||||||
}
|
|
||||||
|
|
||||||
img2.Image? defaultCursorImage = img2.decodePng(base64Decode(
|
|
||||||
'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAFmSURBVFiF7dWxSlxREMbx34QFDRowYBchZSxSCWlMCOwD5FGEFHap06UI7KPsAyyEEIQFqxRaCqYTsqCJFsKkuAeRXb17wrqV918dztw55zszc2fo6Oh47MR/e3zO1/iAHWmznHKGQwx9ip/LEbCfazbsoY8j/JLOhcC6sCW9wsjEwJf483AC9nPNc1+lFRwI13d+l3rYFS799rFGxJMqARv2pBXh+72XQ7gWvklPS7TmMl9Ak/M+DqrENvxAv/guKKApuKPWl0/TROK4+LbSqzhuB+OZ3fRSeFPWY+Fkyn56Y29hfgTSpnQ+s98cvorVey66uPlNFxKwZOYLCGfCs5n9NMYVrsp6mvXSoFqpqYFDvMBkStgJJe93dZOwVXxbqUnBENulydSReqUrDhcX0PT2EXarBYS3GNXMhboinBgIl9K71kg0L3+PvyYGdVpruT2MwrF0iotiXfIwus0Dj+OOjo6Of+e7ab74RkpgAAAAAElFTkSuQmCC'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class QualityMonitorData {
|
class QualityMonitorData {
|
||||||
|
Loading…
Reference in New Issue
Block a user