Files
ttstd_family_care/lib/features/connection/data/protobuf_codec.dart
TongTongStudio 41a514e9cc feat(connection): 添加远程控制路由并更新版本策略
- 新增 /connection 路由,从全局选中设备 SN 读取并渲染远程控制页
- 首页镜像操作改为静默下发 mirror 命令并跳转远程控制页
- 更新 AGENTS.md 版本管理策略为使用最新稳定版本
- 调整 pubspec.lock 中 hooks 与 record_use 版本
2026-08-26 06:16:44 +08:00

140 lines
3.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:typed_data';
/// 控制指令 Action 枚举(与控制端 proto 的 Action 字段号一致)。
class ActionType {
static const unknown = 0;
static const touch = 1;
static const swipe = 2;
static const key = 3;
static const longPress = 4;
static const motionEvent = 5;
static const setResolution = 6;
static const reportResolution = 7;
static const setStreamMode = 8;
static const reportStreamMode = 9;
}
/// protobuf wire format 编码器。
///
/// 与控制端 `com.ttstd.control.ControlMessage`(protobuf 3)字段号完全对齐:
/// action=1, x=2, y=3, x1=4, y1=5, x2=6, y2=7, duration=8,
/// key_code=9, key_action=10, motion_action=11, width=12, height=13,
/// fps=14, stream_mode=15, supported_fps=16(repeated int32)。
///
/// 坐标均为相对屏幕百分比(0.0~1.0),被控端乘以真实分辨率还原像素。
class ControlMessageEncoder {
final int action;
// 单点坐标
final double x;
final double y;
// 滑动起止
final double x1;
final double y1;
final double x2;
final double y2;
final int duration;
// 按键
final int keyCode;
final int keyAction;
// 原始动作
final int motionAction;
// 分辨率/帧率
final int width;
final int height;
final int fps;
final int streamMode;
const ControlMessageEncoder({
required this.action,
this.x = 0,
this.y = 0,
this.x1 = 0,
this.y1 = 0,
this.x2 = 0,
this.y2 = 0,
this.duration = 0,
this.keyCode = 0,
this.keyAction = 0,
this.motionAction = 0,
this.width = 0,
this.height = 0,
this.fps = 0,
this.streamMode = 0,
});
/// 编码为 protobuf 二进制字节数组。
Uint8List encode() {
final out = BytesBuilder(copy: false);
// 1: action (enum=int32)
_writeVarintField(out, 1, action);
// 2: x, 3: y (double, wire type 1)
if (x != 0) _writeFixed64Field(out, 2, x);
if (y != 0) _writeFixed64Field(out, 3, y);
// 4-7: swipe 起止
if (x1 != 0) _writeFixed64Field(out, 4, x1);
if (y1 != 0) _writeFixed64Field(out, 5, y1);
if (x2 != 0) _writeFixed64Field(out, 6, x2);
if (y2 != 0) _writeFixed64Field(out, 7, y2);
// 8: duration (int64)
if (duration != 0) _writeVarintField(out, 8, duration);
// 9: key_code, 10: key_action (int32)
if (keyCode != 0) _writeVarintField(out, 9, keyCode);
if (keyAction != 0) _writeVarintField(out, 10, keyAction);
// 11: motion_action
if (motionAction != 0) _writeVarintField(out, 11, motionAction);
// 12-14: width/height/fps
if (width != 0) _writeVarintField(out, 12, width);
if (height != 0) _writeVarintField(out, 13, height);
if (fps != 0) _writeVarintField(out, 14, fps);
// 15: stream_mode
if (streamMode != 0) _writeVarintField(out, 15, streamMode);
return out.toBytes();
}
// ---- protobuf wire format 原语 ----
void _writeVarintField(BytesBuilder out, int field, int value) {
_writeTag(out, field, 0); // wire type 0 = varint
_writeVarint(out, value);
}
void _writeFixed64Field(BytesBuilder out, int field, double value) {
_writeTag(out, field, 1); // wire type 1 = 64-bit (double)
// protobuf 的 fixed64/double 必须为 little-endian,否则设备端解析出错误值(如 NaN/Infinity)
final bd = ByteData(8);
bd.setFloat64(0, value, Endian.little);
out.add(bd.buffer.asUint8List());
}
void _writeTag(BytesBuilder out, int field, int wireType) {
_writeVarint(out, (field << 3) | wireType);
}
void _writeVarint(BytesBuilder out, int value) {
// 转成无符号 64 位表示,配合逻辑右移 >>>,避免负数算术右移导致死循环(OOM)。
var v = value.toUnsigned(64);
while (true) {
if ((v & ~0x7F) == 0) {
out.addByte(v & 0xFF);
break;
}
out.addByte((v & 0x7F) | 0x80);
v = v >>> 7;
}
}
}