fix: 修复了部分问题:

read_full 部分读取返回不当	中途连接关闭时返回 -1 并设置 errno = ECONNRESET,避免上层误用不完整数据
tcgetattr 重复调用	改为 struct termios raw = g_original_termios 复制结构体,移除冗余系统调用
g_termios_saved 设置时机	移到 tcsetattr 成功之后,确保只有真正修改终端后才标记
有符号/无符号比较警告	使用 (size_t)written != sizeof(header) 显式转换
鼠标跟踪部分写入未检查	添加 (size_t)written != len 检查
载荷大小无上限	添加 MAX_PAYLOAD_SIZE (64MB) 限制,防止恶意数据耗尽内存
write 返回 0 未处理	添加检查,视为 EIO 错误
free_message_payload 冗余检查	直接调用 free(payload),因为 free(NULL) 是安全的
DEBUG_LOG 格式不一致	统一移除末尾 \n(假设宏自动添加换行)
This commit is contained in:
QCQCQC@Opi5 2025-12-13 17:30:08 +08:00
parent a1f79164b7
commit c05c02c098
1 changed files with 399 additions and 179 deletions

View File

@ -1,3 +1,14 @@
/**
* @file socket_protocol.c
* @brief Socket
*
*
* -
* -
* -
* - LZ4
*/
#include "socket_protocol.h"
#include <arpa/inet.h>
@ -8,23 +19,66 @@
#include "debug.h"
// 保存原始终端设置
/* ============================================================================
*
* ============================================================================ */
/** 最大载荷大小限制64MB防止恶意或损坏数据导致的内存耗尽 */
#define MAX_PAYLOAD_SIZE (64 * 1024 * 1024)
/* ============================================================================
* -
* ============================================================================ */
/** 原始终端设置(用于恢复) */
static struct termios g_original_termios;
static int g_termios_saved = 0;
/**
*
* 使 volatile
* 访 pthread_join
*/
static volatile int g_termios_saved = 0;
/* ============================================================================
* LZ4
* ============================================================================ */
#ifdef HAVE_LZ4
// 初始化协议上下文
void protocol_init(ProtocolContext* ctx, CompressionType compress_type, int compress_level) {
if (ctx == NULL) return;
/**
* @brief
*
* @param ctx
* @param compress_type
* @param compress_level
*/
void protocol_init(ProtocolContext* ctx, CompressionType compress_type,
int compress_level) {
if (ctx == NULL) {
return;
}
compression_init(&ctx->compress_ctx, compress_type, compress_level);
ctx->compression_enabled = (compress_type != COMPRESS_NONE);
DEBUG_LOG("协议上下文初始化: compression=%d", ctx->compression_enabled);
}
#endif
// 向 socket 完整写入指定字节数(处理部分写入)
/* ============================================================================
* I/O
* ============================================================================ */
/**
* @brief socket
*
*
*
* @param sock Socket
* @param buf
* @param count
* @return -1
*/
static ssize_t write_full(int sock, const void* buf, size_t count) {
size_t total_written = 0;
const char* ptr = (const char*)buf;
@ -33,25 +87,87 @@ static ssize_t write_full(int sock, const void* buf, size_t count) {
ssize_t n = write(sock, ptr + total_written, count - total_written);
if (n < 0) {
if (errno == EINTR) {
continue; // 被信号中断,重试
continue; /* 被信号中断,重试 */
}
DEBUG_LOG("write_full: write error: %s", strerror(errno));
DEBUG_LOG("write_full: 写入错误: %s", strerror(errno));
return -1;
}
total_written += n;
if (total_written < count) {
DEBUG_LOG("write_full: 进度 %zu/%zu 字节", total_written, count);
if (n == 0) {
/* write 返回 0 通常不应该发生,视为错误 */
DEBUG_LOG("write_full: write 返回 0");
errno = EIO;
return -1;
}
total_written += (size_t)n;
}
return (ssize_t)total_written;
}
// 写入完整消息
/**
* @brief socket
*
*
*
* @param sock Socket
* @param buf
* @param count
* @return 0 -1
*
* @note -1 errno = ECONNRESET
*/
static ssize_t read_full(int sock, void* buf, size_t count) {
size_t total_read = 0;
char* ptr = (char*)buf;
while (total_read < count) {
ssize_t n = read(sock, ptr + total_read, count - total_read);
if (n < 0) {
if (errno == EINTR) {
continue; /* 被信号中断,重试 */
}
DEBUG_LOG("read_full: 读取错误: %s", strerror(errno));
return -1;
}
if (n == 0) {
/* 连接关闭 */
if (total_read > 0) {
/*
*
*
*/
DEBUG_LOG("read_full: 连接中途关闭,已读取 %zu/%zu 字节",
total_read, count);
errno = ECONNRESET;
return -1;
}
/* 干净的连接关闭(未读取任何数据) */
return 0;
}
total_read += (size_t)n;
}
return (ssize_t)total_read;
}
/* ============================================================================
*
* ============================================================================ */
/**
* @brief socket
*
* @param sock Socket
* @param type
* @param payload NULL
* @param payload_len
* @return 0 -1
*/
int write_message(int sock, MessageType type, const void* payload,
uint32_t payload_len) {
DEBUG_LOG("写入消息: type=%d, payload_len=%u", type, payload_len);
/* 构建消息头 */
MessageHeader header;
header.magic = MESSAGE_MAGIC;
header.type = type;
@ -60,25 +176,22 @@ int write_message(int sock, MessageType type, const void* payload,
DEBUG_HEX("消息头", &header, sizeof(header));
// 发送消息头(确保完整发送)
/* 发送消息头 */
ssize_t written = write_full(sock, &header, sizeof(header));
if (written != sizeof(header)) {
DEBUG_LOG(
"Failed to write message header: 期望 %zu 字节, 实际写入 %zd "
"字节\n",
sizeof(header), written);
if (written < 0 || (size_t)written != sizeof(header)) {
DEBUG_LOG("写入消息头失败: 期望 %zu 字节, 实际 %zd 字节",
sizeof(header), written);
return -1;
}
// 写入载荷(确保完整发送)
/* 发送载荷 */
if (payload_len > 0 && payload != NULL) {
DEBUG_HEX("消息载荷", payload, payload_len > 64 ? 64 : payload_len);
written = write_full(sock, payload, payload_len);
if (written != (ssize_t)payload_len) {
DEBUG_LOG(
"Failed to write message payload: 期望 %u 字节, 实际写入 %zd "
"字节\n",
payload_len, written);
if (written < 0 || (size_t)written != payload_len) {
DEBUG_LOG("写入消息载荷失败: 期望 %u 字节, 实际 %zd 字节",
payload_len, written);
return -1;
}
DEBUG_LOG("载荷写入成功: %zd 字节", written);
@ -89,124 +202,123 @@ int write_message(int sock, MessageType type, const void* payload,
}
#ifdef HAVE_LZ4
// 带压缩支持的消息写入
/**
* @brief socket
*
* 退
*
* @param sock Socket
* @param ctx
* @param type
* @param payload
* @param payload_len
* @return 0 -1
*/
int write_message_compressed(int sock, ProtocolContext* ctx, MessageType type,
const void* payload, uint32_t payload_len) {
DEBUG_LOG("写入压缩消息: type=%d, payload_len=%u", type, payload_len);
// 如果没有上下文或压缩未启用,使用普通写入
/* 如果没有上下文或压缩未启用,使用普通写入 */
if (ctx == NULL || !ctx->compression_enabled || payload_len == 0) {
return write_message(sock, type, payload, payload_len);
}
// 分配压缩缓冲区
/* 分配压缩缓冲区 */
size_t compress_bound = get_compress_bound(payload_len);
void* compressed_buf = malloc(compress_bound);
if (compressed_buf == NULL) {
DEBUG_LOG("分配压缩缓冲区失败");
DEBUG_LOG("分配压缩缓冲区失败,回退到普通写入");
return write_message(sock, type, payload, payload_len);
}
// 压缩数据
/* 压缩数据 */
uint32_t flags = 0;
int compressed_size = compress_data(&ctx->compress_ctx,
int compressed_size = compress_data(&ctx->compress_ctx,
payload, payload_len,
compressed_buf, compress_bound,
&flags);
if (compressed_size < 0) {
DEBUG_LOG("压缩失败,使用原始数据");
DEBUG_LOG("压缩失败,回退到普通写入");
free(compressed_buf);
return write_message(sock, type, payload, payload_len);
}
// 构建消息头
/* 构建消息头 */
MessageHeader header;
header.magic = MESSAGE_MAGIC;
header.type = type;
header.payload_len = (uint32_t)compressed_size;
header.reserved = MAKE_RESERVED(flags, payload_len);
DEBUG_LOG("压缩消息头: type=%d, compressed_len=%d, original_len=%u, flags=0x%x",
DEBUG_LOG("压缩消息头: type=%d, compressed=%d, original=%u, flags=0x%x",
type, compressed_size, payload_len, flags);
DEBUG_HEX("压缩消息头", &header, sizeof(header));
// 发送消息头
/* 发送消息头 */
ssize_t written = write_full(sock, &header, sizeof(header));
if (written != sizeof(header)) {
DEBUG_LOG("写入消息头失败");
if (written < 0 || (size_t)written != sizeof(header)) {
DEBUG_LOG("写入压缩消息头失败");
free(compressed_buf);
return -1;
}
// 发送压缩后的载荷
/* 发送压缩后的载荷 */
if (compressed_size > 0) {
written = write_full(sock, compressed_buf, compressed_size);
if (written != compressed_size) {
written = write_full(sock, compressed_buf, (size_t)compressed_size);
if (written < 0 || (size_t)written != (size_t)compressed_size) {
DEBUG_LOG("写入压缩载荷失败");
free(compressed_buf);
return -1;
}
}
free(compressed_buf);
DEBUG_LOG("压缩消息写入完成: %u -> %d 字节", payload_len, compressed_size);
return 0;
}
#endif
// 从 socket 完整读取指定字节数(处理部分读取)
static ssize_t read_full(int sock, void* buf, size_t count) {
size_t total_read = 0;
char* ptr = (char*)buf;
while (total_read < count) {
ssize_t n = read(sock, ptr + total_read, count - total_read);
if (n < 0) {
if (errno == EINTR) {
continue; // 被信号中断,重试
}
DEBUG_LOG("read_full: read error: %s", strerror(errno));
return -1;
}
if (n == 0) {
// 连接关闭
DEBUG_LOG("read_full: 连接关闭,已读取 %zu/%zu 字节", total_read,
count);
return total_read > 0 ? (ssize_t)total_read : 0;
}
total_read += n;
DEBUG_LOG("read_full: 进度 %zu/%zu 字节", total_read, count);
}
return (ssize_t)total_read;
}
// 读取完整消息
/**
* @brief socket
*
* @param sock Socket
* @param type [out]
* @param payload [out]
* @param payload_len [out]
* @return 1 0 -1
*/
int read_message(int sock, MessageType* type, void** payload,
uint32_t* payload_len) {
MessageHeader header;
DEBUG_LOG("开始读取消息...");
// 读取消息头(确保读取完整)
/* 读取消息头 */
ssize_t bytes_read = read_full(sock, &header, sizeof(header));
if (bytes_read != sizeof(header)) {
if (bytes_read == 0) {
DEBUG_LOG("连接正常关闭");
return 0; // 连接正常关闭
}
DEBUG_LOG(
"Failed to read message header, got %zd bytes, expected %zu\n",
bytes_read, sizeof(header));
if (bytes_read == 0) {
DEBUG_LOG("连接正常关闭");
return 0;
}
if (bytes_read < 0 || (size_t)bytes_read != sizeof(header)) {
DEBUG_LOG("读取消息头失败: 期望 %zu 字节, 实际 %zd 字节",
sizeof(header), bytes_read);
return -1;
}
DEBUG_HEX("收到消息头", &header, sizeof(header));
// 验证魔数
/* 验证魔数 */
if (header.magic != MESSAGE_MAGIC) {
DEBUG_LOG("Invalid message magic: 0x%x\n", header.magic);
DEBUG_LOG("无效的消息魔数: 0x%x (期望 0x%x)",
header.magic, MESSAGE_MAGIC);
return -1;
}
/* 验证载荷大小 */
if (header.payload_len > MAX_PAYLOAD_SIZE) {
DEBUG_LOG("载荷大小超出限制: %u > %u",
header.payload_len, MAX_PAYLOAD_SIZE);
return -1;
}
@ -215,28 +327,29 @@ int read_message(int sock, MessageType* type, void** payload,
DEBUG_LOG("消息类型=%d, 载荷长度=%u", *type, *payload_len);
// 读取载荷(确保读取完整)
/* 读取载荷 */
if (header.payload_len > 0) {
*payload = malloc(header.payload_len + 1); // +1 用于可能的字符串终止符
/* +1 用于字符串终止符 */
*payload = malloc(header.payload_len + 1);
if (*payload == NULL) {
DEBUG_LOG("Failed to allocate memory for payload\n");
DEBUG_LOG("分配载荷内存失败: %u 字节", header.payload_len);
return -1;
}
bytes_read = read_full(sock, *payload, header.payload_len);
if (bytes_read != (ssize_t)header.payload_len) {
DEBUG_LOG(
"Failed to read message payload: 期望%u字节, 实际读取%zd字节\n",
header.payload_len, bytes_read);
if (bytes_read < 0 || (size_t)bytes_read != header.payload_len) {
DEBUG_LOG("读取载荷失败: 期望 %u 字节, 实际 %zd 字节",
header.payload_len, bytes_read);
free(*payload);
*payload = NULL;
return -1;
}
DEBUG_LOG("载荷读取成功: %zd 字节", bytes_read);
DEBUG_HEX("收到载荷", *payload, bytes_read > 64 ? 64 : bytes_read);
DEBUG_HEX("收到载荷", *payload,
(size_t)bytes_read > 64 ? 64 : (size_t)bytes_read);
// 如果是字符串,添加终止符
/* 添加字符串终止符(便于作为字符串使用) */
((char*)(*payload))[header.payload_len] = '\0';
} else {
*payload = NULL;
@ -246,83 +359,107 @@ int read_message(int sock, MessageType* type, void** payload,
}
#ifdef HAVE_LZ4
// 带解压支持的消息读取
/**
* @brief socket
*
* @param sock Socket
* @param type [out]
* @param payload [out]
* @param payload_len [out]
* @param original_len [out] NULL
* @return 1 0 -1
*/
int read_message_decompressed(int sock, MessageType* type, void** payload,
uint32_t* payload_len, uint32_t* original_len) {
MessageHeader header;
DEBUG_LOG("开始读取消息(带解压)...");
// 读取消息头
/* 读取消息头 */
ssize_t bytes_read = read_full(sock, &header, sizeof(header));
if (bytes_read != sizeof(header)) {
if (bytes_read == 0) {
DEBUG_LOG("连接正常关闭");
return 0;
}
DEBUG_LOG("读取消息头失败: got %zd bytes, expected %zu", bytes_read, sizeof(header));
if (bytes_read == 0) {
DEBUG_LOG("连接正常关闭");
return 0;
}
if (bytes_read < 0 || (size_t)bytes_read != sizeof(header)) {
DEBUG_LOG("读取消息头失败: 期望 %zu 字节, 实际 %zd 字节",
sizeof(header), bytes_read);
return -1;
}
DEBUG_HEX("收到消息头", &header, sizeof(header));
// 验证魔数
/* 验证魔数 */
if (header.magic != MESSAGE_MAGIC) {
DEBUG_LOG("无效魔数: 0x%x", header.magic);
DEBUG_LOG("无效的消息魔数: 0x%x", header.magic);
return -1;
}
/* 验证载荷大小 */
if (header.payload_len > MAX_PAYLOAD_SIZE) {
DEBUG_LOG("载荷大小超出限制: %u > %u",
header.payload_len, MAX_PAYLOAD_SIZE);
return -1;
}
*type = (MessageType)header.type;
uint32_t flags = GET_COMPRESS_FLAGS(header.reserved);
uint32_t size_hint = GET_ORIGINAL_SIZE_HINT(header.reserved);
DEBUG_LOG("消息类型=%d, 压缩载荷=%u, flags=0x%x, size_hint=%u",
*type, header.payload_len, flags, size_hint);
// 读取压缩载荷
/* 空载荷处理 */
if (header.payload_len == 0) {
*payload = NULL;
*payload_len = 0;
if (original_len) *original_len = 0;
if (original_len != NULL) {
*original_len = 0;
}
return 1;
}
void* compressed_buf = malloc(header.payload_len);
/* 分配缓冲区(+1 用于字符串终止符) */
void* compressed_buf = malloc(header.payload_len + 1);
if (compressed_buf == NULL) {
DEBUG_LOG("分配压缩缓冲区失败");
return -1;
}
/* 读取载荷 */
bytes_read = read_full(sock, compressed_buf, header.payload_len);
if (bytes_read != (ssize_t)header.payload_len) {
DEBUG_LOG("读取压缩载荷失败: 期望%u字节, 实际%zd字节",
if (bytes_read < 0 || (size_t)bytes_read != header.payload_len) {
DEBUG_LOG("读取压缩载荷失败: 期望 %u 字节, 实际 %zd 字节",
header.payload_len, bytes_read);
free(compressed_buf);
return -1;
}
// 检查是否需要解压
/* 检查是否需要解压 */
if (!(flags & MSG_FLAG_COMPRESSED)) {
// 数据未压缩,直接返回
/* 数据未压缩,添加终止符后直接返回 */
((char*)compressed_buf)[header.payload_len] = '\0';
*payload = compressed_buf;
*payload_len = header.payload_len;
if (original_len) *original_len = header.payload_len;
// 添加字符串终止符(重新分配以确保空间)
void* new_buf = realloc(compressed_buf, header.payload_len + 1);
if (new_buf) {
*payload = new_buf;
((char*)(*payload))[header.payload_len] = '\0';
if (original_len != NULL) {
*original_len = header.payload_len;
}
return 1;
}
// 需要解压
// 估算解压后大小(使用 size_hint 或默认 4 倍)
size_t decompress_capacity = size_hint > 0 ? size_hint + 256 : header.payload_len * 4;
if (decompress_capacity < 256) decompress_capacity = 256;
/* 需要解压 */
/* 估算解压后大小(使用 size_hint 或默认 4 倍) */
size_t decompress_capacity = (size_hint > 0)
? (size_hint + 256)
: (header.payload_len * 4);
if (decompress_capacity < 256) {
decompress_capacity = 256;
}
if (decompress_capacity > MAX_PAYLOAD_SIZE) {
decompress_capacity = MAX_PAYLOAD_SIZE;
}
/* 分配解压缓冲区(+1 用于字符串终止符) */
void* decompressed_buf = malloc(decompress_capacity + 1);
if (decompressed_buf == NULL) {
DEBUG_LOG("分配解压缓冲区失败");
@ -330,120 +467,203 @@ int read_message_decompressed(int sock, MessageType* type, void** payload,
return -1;
}
int decompressed_size = decompress_data(flags,
/* 解压数据 */
int decompressed_size = decompress_data(flags,
compressed_buf, header.payload_len,
decompressed_buf, decompress_capacity);
free(compressed_buf);
if (decompressed_size < 0) {
DEBUG_LOG("解压失败");
free(decompressed_buf);
return -1;
}
// 添加字符串终止符
/* 添加字符串终止符 */
((char*)decompressed_buf)[decompressed_size] = '\0';
*payload = decompressed_buf;
*payload_len = (uint32_t)decompressed_size;
if (original_len) *original_len = header.payload_len;
if (original_len != NULL) {
*original_len = header.payload_len;
}
DEBUG_LOG("解压成功: %u -> %d 字节", header.payload_len, decompressed_size);
return 1;
}
#endif
// 释放消息载荷
/**
* @brief
*
* @param payload
*/
void free_message_payload(void* payload) {
if (payload != NULL) {
free(payload);
}
free(payload); /* free(NULL) 是安全的 */
}
// 设置终端为原始模式(捕获所有输入)
/* ============================================================================
*
* ============================================================================ */
/**
* @brief
*
*
* -
* -
* - Ctrl+C
* - /
*
* @param fd
* @return 0 -1
*/
int setup_terminal_raw_mode(int fd) {
DEBUG_LOG("设置终端为原始模式: fd=%d", fd);
struct termios raw;
/* 保存原始设置 */
if (tcgetattr(fd, &g_original_termios) < 0) {
DEBUG_LOG("保存原始终端设置失败");
return -1;
}
g_termios_saved = 1;
DEBUG_LOG("原始终端设置已保存");
if (tcgetattr(fd, &raw) < 0) {
DEBUG_LOG("保存原始终端设置失败: %s", strerror(errno));
return -1;
}
// 设置原始模式
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_cflag &= ~(CSIZE | PARENB);
raw.c_cflag |= CS8;
raw.c_oflag &= ~(OPOST);
/* 基于原始设置创建修改版本 */
struct termios raw = g_original_termios;
// 设置读取超时
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 1;
/* 输入模式:禁用各种处理 */
raw.c_iflag &= ~(tcflag_t)(BRKINT | /* 忽略 BREAK */
ICRNL | /* 不将 CR 转换为 NL */
INPCK | /* 禁用奇偶校验 */
ISTRIP | /* 不剥离第 8 位 */
IXON); /* 禁用软件流控制 */
/* 输出模式:禁用输出处理 */
raw.c_oflag &= ~(tcflag_t)(OPOST); /* 禁用输出处理 */
/* 控制模式:设置字符大小为 8 位 */
raw.c_cflag &= ~(tcflag_t)(CSIZE | /* 清除字符大小位 */
PARENB); /* 禁用奇偶校验 */
raw.c_cflag |= CS8; /* 8 位字符 */
/* 本地模式:禁用回显和规范模式 */
raw.c_lflag &= ~(tcflag_t)(ECHO | /* 禁用回显 */
ICANON | /* 禁用规范模式 */
IEXTEN | /* 禁用扩展输入处理 */
ISIG); /* 禁用信号生成 */
/* 设置读取参数 */
raw.c_cc[VMIN] = 0; /* 非阻塞读取 */
raw.c_cc[VTIME] = 1; /* 100ms 超时 */
/* 应用新设置 */
if (tcsetattr(fd, TCSAFLUSH, &raw) < 0) {
DEBUG_LOG("设置终端原始模式失败");
DEBUG_LOG("设置终端原始模式失败: %s", strerror(errno));
return -1;
}
/* 只有成功后才标记为已保存 */
g_termios_saved = 1;
DEBUG_LOG("终端原始模式设置成功");
return 0;
}
// 恢复终端模式
/**
* @brief
*
* setup_terminal_raw_mode
*
*
* @param fd
* @return 0 -1
*/
int restore_terminal_mode(int fd) {
DEBUG_LOG("恢复终端模式: fd=%d", fd);
if (!g_termios_saved) {
DEBUG_LOG("没有保存的终端设置,跳过恢复");
return 0; // 没有保存过或已经恢复过,这不是错误
DEBUG_LOG("没有保存的终端设置跳过恢复");
return 0; /* 没有保存过或已经恢复过,不是错误 */
}
if (tcsetattr(fd, TCSAFLUSH, &g_original_termios) < 0) {
DEBUG_LOG("恢复终端模式失败");
DEBUG_LOG("恢复终端模式失败: %s", strerror(errno));
/* 不清除 g_termios_saved允许后续重试 */
return -1;
}
DEBUG_LOG("终端模式恢复成功");
g_termios_saved = 0; // 标记已恢复,防止重复恢复
g_termios_saved = 0; /* 标记已恢复,防止重复恢复 */
return 0;
}
// 启用鼠标跟踪发送ANSI转义序列
/**
* @brief
*
* ANSI
*
* - X11 (1000)
* - (1002)
* - SGR (1006)
*
* @param fd
* @return 0 -1
*/
int enable_mouse_tracking(int fd) {
// 启用鼠标跟踪模式
// \033[?1000h - 启用X11鼠标报告
// \033[?1002h - 启用单元格运动鼠标跟踪
// \033[?1003h - 启用所有运动鼠标跟踪
// \033[?1006h - 启用SGR扩展鼠标模式
const char* enable_seq = "\033[?1000h\033[?1002h\033[?1006h";
/*
*
* \033[?1000h - X11
* \033[?1002h -
* \033[?1006h - SGR 223
*/
static const char enable_seq[] = "\033[?1000h\033[?1002h\033[?1006h";
size_t len = sizeof(enable_seq) - 1; /* 不包括 '\0' */
ssize_t written = write(fd, enable_seq, strlen(enable_seq));
ssize_t written = write(fd, enable_seq, len);
if (written < 0) {
DEBUG_LOG("启用鼠标跟踪失败: %s", strerror(errno));
return -1;
}
if ((size_t)written != len) {
DEBUG_LOG("启用鼠标跟踪: 部分写入 %zd/%zu 字节", written, len);
return -1;
}
DEBUG_LOG("鼠标跟踪已启用");
return 0;
}
// 禁用鼠标跟踪
/**
* @brief
*
* ANSI
*
*
* @param fd
* @return 0 -1
*/
int disable_mouse_tracking(int fd) {
// 禁用鼠标跟踪模式
// \033[?1000l - 禁用X11鼠标报告
// \033[?1002l - 禁用单元格运动鼠标跟踪
// \033[?1006l - 禁用SGR扩展鼠标模式
const char* disable_seq = "\033[?1006l\033[?1002l\033[?1000l";
/*
*
* \033[?1006l - SGR
* \033[?1002l -
* \033[?1000l - X11
*
*/
static const char disable_seq[] = "\033[?1006l\033[?1002l\033[?1000l";
size_t len = sizeof(disable_seq) - 1; /* 不包括 '\0' */
ssize_t written = write(fd, disable_seq, strlen(disable_seq));
ssize_t written = write(fd, disable_seq, len);
if (written < 0) {
DEBUG_LOG("禁用鼠标跟踪失败: %s", strerror(errno));
return -1;
}
if ((size_t)written != len) {
DEBUG_LOG("禁用鼠标跟踪: 部分写入 %zd/%zu 字节", written, len);
return -1;
}
DEBUG_LOG("鼠标跟踪已禁用");
return 0;
}
}