在使用ubuntu24.04作为我工作的主系统,有时有远程操作办公的需求,ubuntu提供了与windows兼容的不错的方案,在设置-》系统-》远程桌面,进行配置就可以使用远程桌面连接进行远程操作;与windwos的远程桌面工具兼容,操作一致。
但是也有不完善的地方,让这个功能有时就是鸡肋。由于该功能要在登陆后才可以用,而且不可以锁屏状态下连接,所以在你需要要远程使用时,基本用不了(不可能设置不锁屏,不安全;也不可能把密码告诉同事让帮忙登陆或解锁屏幕,更不安全)
但是远程操作又是时常的需求,所以我想用给系统发按键事件的方式,让系统锁屏界面像用户输入密码一样,解锁屏幕。
源码实现如下:
cpp
#include <iostream>
#include <linux/input.h>
#include <fcntl.h> // 修复 O_RDWR 未定义问题
#include <stdio.h> // 用于 printf
#include <sys/time.h> // 用于 gettimeofday
#include <unistd.h> // 用于 write 和 open
#include <unordered_map>
#define KEYDOWN(fd_kb,event,key) \
do \
{ \
event.code = key; \
event.type = EV_KEY; \
event.value = 1; \
gettimeofday(&event.time, NULL);\
if (write(fd_kb, &event, sizeof(event)) != sizeof(event))\
{\
printf("write /dev/input/event0 failed\n");\
}\
} while (0);
#define KEYUP(fd_kb,event,key) \
do \
{ \
event.code = key; \
event.type = EV_KEY; \
event.value = 0; \
gettimeofday(&event.time, NULL);\
if (write(fd_kb, &event, sizeof(event)) != sizeof(event))\
{\
printf("write /dev/input/event0 failed\n");\
}\
} while (0);
#define KEYSYNC(fd_kb,event) \
do \
{ \
event.code = EV_SYN; \
event.type = SYN_REPORT; \
event.value = 0; \
gettimeofday(&event.time, NULL);\
if (write(fd_kb, &event, sizeof(event)) != sizeof(event))\
{\
printf("write /dev/input/event0 failed\n");\
}\
} while (0);
// struct input_event g_event;
// int keyDown(int fd_kb, __u16 key)
// {
// // struct input_event event;
// g_event.code = key;
// g_event.type = EV_KEY;
// g_event.value = 1; //1表示按下,0表示释放,2表示长按
// gettimeofday(&g_event.time, NULL);
// if (write(fd_kb, &g_event, sizeof(g_event)) != sizeof(g_event))
// {
// printf("write /dev/input/event0 failed\n");
// }
// }
// int keyUp(int fd_kb, __u16 key)
// {
// // struct input_event event;
// g_event.code = key;
// g_event.type = EV_KEY;
// g_event.value = 0; //1表示按下,0表示释放,2表示长按
// gettimeofday(&g_event.time, NULL);
// if (write(fd_kb, &g_event, sizeof(g_event)) != sizeof(g_event))
// {
// printf("write /dev/input/event0 failed\n");
// }
// }
// int keySync(int fd_kb)
// {
// // struct input_event event;
// g_event.type = EV_SYN;
// g_event.code = SYN_REPORT;
// g_event.value = 0;
// gettimeofday(&g_event.time, NULL);
// if (write(fd_kb, &g_event, sizeof(g_event)) != sizeof(g_event)) {
// printf("写入同步事件失败");
// }
// }
// ASCII码与KEY定义的对应表
std::unordered_map<int, int> asciiToKeyMap = {
// 控制字符 (0-31)
{0, KEY_RESERVED}, // NUL
{1, KEY_RESERVED}, // SOH
{2, KEY_RESERVED}, // STX
{3, KEY_RESERVED}, // ETX
{4, KEY_RESERVED}, // EOT
{5, KEY_RESERVED}, // ENQ
{6, KEY_RESERVED}, // ACK
{7, KEY_RESERVED}, // BEL
{8, KEY_BACKSPACE}, // BS
{9, KEY_TAB}, // TAB
{10, KEY_ENTER}, // LF (换行)
{11, KEY_RESERVED}, // VT
{12, KEY_RESERVED}, // FF
{13, KEY_ENTER}, // CR (回车)
{14, KEY_RESERVED}, // SO
{15, KEY_RESERVED}, // SI
{16, KEY_RESERVED}, // DLE
{17, KEY_RESERVED}, // DC1
{18, KEY_RESERVED}, // DC2
{19, KEY_RESERVED}, // DC3
{20, KEY_RESERVED}, // DC4
{21, KEY_RESERVED}, // NAK
{22, KEY_RESERVED}, // SYN
{23, KEY_RESERVED}, // ETB
{24, KEY_RESERVED}, // CAN
{25, KEY_RESERVED}, // EM
{26, KEY_RESERVED}, // SUB
{27, KEY_ESC}, // ESC
{28, KEY_RESERVED}, // FS
{29, KEY_RESERVED}, // GS
{30, KEY_RESERVED}, // RS
{31, KEY_RESERVED}, // US
// 空格和标点符号 (32-64)
{32, KEY_SPACE}, // 空格
{33, KEY_1}, // ! (需要Shift)
{34, KEY_APOSTROPHE}, // " (需要Shift)
{35, KEY_3}, // # (需要Shift)
{36, KEY_4}, // $ (需要Shift)
{37, KEY_5}, // % (需要Shift)
{38, KEY_7}, // & (需要Shift)
{39, KEY_APOSTROPHE}, // '
{40, KEY_9}, // ( (需要Shift)
{41, KEY_0}, // ) (需要Shift)
{42, KEY_8}, // * (需要Shift)
{43, KEY_EQUAL}, // + (需要Shift)
{44, KEY_COMMA}, // ,
{45, KEY_MINUS}, // -
{46, KEY_DOT}, // .
{47, KEY_SLASH}, // /
// 数字 (48-57)
{48, KEY_0}, // 0
{49, KEY_1}, // 1
{50, KEY_2}, // 2
{51, KEY_3}, // 3
{52, KEY_4}, // 4
{53, KEY_5}, // 5
{54, KEY_6}, // 6
{55, KEY_7}, // 7
{56, KEY_8}, // 8
{57, KEY_9}, // 9
// 标点符号 (58-64)
{58, KEY_SEMICOLON}, // : (需要Shift)
{59, KEY_SEMICOLON}, // ;
{60, KEY_COMMA}, // < (需要Shift)
{61, KEY_EQUAL}, // =
{62, KEY_DOT}, // > (需要Shift)
{63, KEY_SLASH}, // ? (需要Shift)
{64, KEY_2}, // @ (需要Shift)
// 大写字母 (65-90)
{65, KEY_A}, // A (需要Shift)
{66, KEY_B}, // B (需要Shift)
{67, KEY_C}, // C (需要Shift)
{68, KEY_D}, // D (需要Shift)
{69, KEY_E}, // E (需要Shift)
{70, KEY_F}, // F (需要Shift)
{71, KEY_G}, // G (需要Shift)
{72, KEY_H}, // H (需要Shift)
{73, KEY_I}, // I (需要Shift)
{74, KEY_J}, // J (需要Shift)
{75, KEY_K}, // K (需要Shift)
{76, KEY_L}, // L (需要Shift)
{77, KEY_M}, // M (需要Shift)
{78, KEY_N}, // N (需要Shift)
{79, KEY_O}, // O (需要Shift)
{80, KEY_P}, // P (需要Shift)
{81, KEY_Q}, // Q (需要Shift)
{82, KEY_R}, // R (需要Shift)
{83, KEY_S}, // S (需要Shift)
{84, KEY_T}, // T (需要Shift)
{85, KEY_U}, // U (需要Shift)
{86, KEY_V}, // V (需要Shift)
{87, KEY_W}, // W (需要Shift)
{88, KEY_X}, // X (需要Shift)
{89, KEY_Y}, // Y (需要Shift)
{90, KEY_Z}, // Z (需要Shift)
// 标点符号 (91-96)
{91, KEY_LEFTBRACE}, // [
{92, KEY_BACKSLASH}, // \
{93, KEY_RIGHTBRACE}, // ]
{94, KEY_6}, // ^ (需要Shift)
{95, KEY_MINUS}, // _ (需要Shift)
{96, KEY_GRAVE}, // `
// 小写字母 (97-122)
{97, KEY_A}, // a
{98, KEY_B}, // b
{99, KEY_C}, // c
{100, KEY_D}, // d
{101, KEY_E}, // e
{102, KEY_F}, // f
{103, KEY_G}, // g
{104, KEY_H}, // h
{105, KEY_I}, // i
{106, KEY_J}, // j
{107, KEY_K}, // k
{108, KEY_L}, // l
{109, KEY_M}, // m
{110, KEY_N}, // n
{111, KEY_O}, // o
{112, KEY_P}, // p
{113, KEY_Q}, // q
{114, KEY_R}, // r
{115, KEY_S}, // s
{116, KEY_T}, // t
{117, KEY_U}, // u
{118, KEY_V}, // v
{119, KEY_W}, // w
{120, KEY_X}, // x
{121, KEY_Y}, // y
{122, KEY_Z}, // z
// 标点符号 (123-126)
{123, KEY_LEFTBRACE}, // { (需要Shift)
{124, KEY_BACKSLASH}, // | (需要Shift)
{125, KEY_RIGHTBRACE}, // } (需要Shift)
{126, KEY_GRAVE}, // ~ (需要Shift)
// 删除键
{127, KEY_DELETE} // DEL
};
// 判断字符是否需要Shift键的函数
bool needsShift(int asciiCode) {
// 需要Shift键的ASCII字符范围
if ((asciiCode >= 33 && asciiCode <= 38) || // !"#$%&
(asciiCode >= 40 && asciiCode <= 43) || // )*+
(asciiCode == 58) || (asciiCode == 60) || // :<
(asciiCode == 62) || (asciiCode == 63) || // >?
(asciiCode == 64) || // @
(asciiCode >= 65 && asciiCode <= 90) || // A-Z
(asciiCode == 94) || (asciiCode == 95) || // ^_
(asciiCode == 123) || (asciiCode == 124) || // {|
(asciiCode == 125) || (asciiCode == 126)) { // }~
return true;
}
return false;
}
// 根据ASCII码获取对应的KEY定义
int getKeyCode(int asciiCode) {
auto it = asciiToKeyMap.find(asciiCode);
if (it != asciiToKeyMap.end()) {
return it->second;
}
return KEY_RESERVED; // 未找到对应的键值
}
// 打印ASCII码与KEY定义的对应表
void printAsciiKeyTable() {
std::cout << "ASCII码与KEY定义对应表:\n";
std::cout << "========================================\n";
std::cout << "ASCII | 字符 | KEY定义 | 需要Shift\n";
std::cout << "----------------------------------------\n";
for (int i = 0; i <= 127; i++) {
if (asciiToKeyMap.find(i) != asciiToKeyMap.end()) {
std::cout << i << "\t| ";
// 显示可打印字符
if (i >= 32 && i <= 126) {
std::cout << "'" << (char)i << "'";
} else {
std::cout << "控制字符";
}
std::cout << "\t| KEY_" << asciiToKeyMap[i] << "\t| ";
std::cout << (needsShift(i) ? "是" : "否") << std::endl;
}
}
}
// 使用示例函数:根据字符串模拟按键
void simulateStringInput(int fd_kb, const std::string& text) {
struct input_event event;
for (char c : text) {
int asciiCode = (int)c;
int keyCode = getKeyCode(asciiCode);
bool shiftNeeded = needsShift(asciiCode);
if (keyCode != KEY_RESERVED) {
// 如果需要Shift键,先按下Shift
if (shiftNeeded) {
KEYDOWN(fd_kb, event, KEY_LEFTSHIFT);
}
// 按下并释放字符键
KEYDOWN(fd_kb, event, keyCode);
KEYSYNC(fd_kb, event);
usleep(50 * 1000); // 50ms延迟
KEYUP(fd_kb, event, keyCode);
KEYSYNC(fd_kb, event);
usleep(50 * 1000);
// 如果需要Shift键,释放Shift
if (shiftNeeded) {
KEYUP(fd_kb, event, KEY_LEFTSHIFT);
KEYSYNC(fd_kb, event);
usleep(50 * 1000);
}
}
}
}
int main(int argc, char** argv){
std::cout << "Hello, from kdbunlook!\n";
if (argc != 3)
{
std::cout << "sudo kdbunlook <path-event-kdb> <password>\n";
return -1;
}
std::string userInput = argv[2];
// std::getline(std::cin, userInput);
int fd_kb;
// char *dev_path = "/dev/input/by-path/pci-0000:00:14.0-usb-0:2:1.0-event-kbd";
// char *dev_path = "/dev/input/by-path/pci-0000:00:14.0-usb-0:6:1.1-event-kbd";
// char *dev_path = "/dev/input/by-path/pci-0000:00:14.0-usbv2-0:4:1.0-event-kbd";
char *dev_path = argv[1];
sleep(5);
/* open keyboard */
// fd_kb = open("/dev/input/event5", O_RDWR);
fd_kb = open(dev_path, O_RDWR | O_SYNC);
if (fd_kb < 0) {
printf("Failed to open /dev/input/event0\n");
return 1;
}
simulateStringInput(fd_kb, "\n" + userInput + "\n");
close(fd_kb); // 关闭设备描述符
sleep(1);
return 0;
}
编译后需要sudo权限执行,因为需要操作设备文件。
当你需要远程桌面连接,先需要解锁屏幕,用ssh连接ubuntu系统,执行编译好的程序,带上键盘输入设备文件路径(可以用ls /dev/input/by-path查看)和密码
sudo kdbunlock /dev/input/by-path/pci-0000:00:14.0-usbv2-0:4:1.0-event-kbd xxxx438
延时几秒后就会解锁屏幕(如果大写键已经锁定,会输入不一样,会失败)
然后就可以开心的远程办公了:)