fix: 修复 CapsLock 长按松开后未恢复大小写状态的问题

- 长按松开后调用 simulateCapsLock 模拟一次 CapsLock 按键恢复原始状态
- 新增 simulateKeysym 方法,正确处理 X11 keysym → keycode 转换
- Windows 端添加 X11 keysym → VK 虚拟键码映射

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Alvin Young 2026-06-11 13:45:18 +08:00
parent 8c2e787a25
commit 271fccb39b
5 changed files with 46 additions and 3 deletions

View File

@ -152,6 +152,8 @@ void VoiceInputService::onHotkeyDeactivated() {
} else {
// 长按 → 停止录音并转写
stopRecordingAndTranscribe();
// 恢复 CapsLock 原始状态(系统已处理了一次 CapsLock 切换)
simulateCapsLock();
}
longPressDetected_ = false;
@ -213,9 +215,8 @@ void VoiceInputService::onRecognitionComplete(const QString& text) {
void VoiceInputService::simulateCapsLock() {
if (impl_->injector && impl_->injector->isInitialized()) {
// CapsLock keysym = 0xffe5
unsigned int capslockKeysym = 0xffe5;
impl_->injector->simulateKeycode(capslockKeysym);
// XK_Caps_Lock = 0xffe5使用 simulateKeysym 自动转换为 keycode
impl_->injector->simulateKeysym(0xffe5);
LOG_DEBUG(kTag, "模拟 CapsLock 按键已注入");
} else {
LOG_WARNING(kTag, "文本注入器未初始化,无法模拟 CapsLock");

View File

@ -178,4 +178,17 @@ bool WaylandTextInjector::simulateKeycode(unsigned int keycode) {
return true;
}
bool WaylandTextInjector::simulateKeysym(unsigned long keysym) {
if (!impl_->display || !impl_->XKeysymToKeycode) return false;
unsigned int keycode = impl_->XKeysymToKeycode(impl_->display, keysym);
if (keycode == 0) {
LOG_WARNING(kTag, QString("keysym 0x%1 无法转换为 keycode").arg(keysym, 0, 16));
return false;
}
LOG_DEBUG(kTag, QString("模拟 keysym 0x%1 → keycode %2").arg(keysym, 0, 16).arg(keycode));
return simulateKeycode(keycode);
}
} // namespace impress

View File

@ -30,6 +30,9 @@ public:
/** @brief 模拟 X11 keycode 按下+释放(用于 CapsLock 等系统按键) */
bool simulateKeycode(unsigned int keycode);
/** @brief 模拟 keysym 按下+释放(自动转换为 keycode */
bool simulateKeysym(unsigned long keysym);
signals:
void error(const QString& message);

View File

@ -111,4 +111,27 @@ bool WaylandTextInjector::simulateKeycode(unsigned int keycode) {
#endif
}
bool WaylandTextInjector::simulateKeysym(unsigned long keysym) {
#ifdef Q_OS_WIN
// X11 keysym → Windows Virtual Key 映射
WORD vk = 0;
switch (keysym) {
case 0xffe5: vk = 0x14; break; // XK_Caps_Lock → VK_CAPITAL
case 0xffe1: vk = 0x10; break; // XK_Shift_L → VK_SHIFT
case 0xffe2: vk = 0x10; break; // XK_Shift_R → VK_SHIFT
case 0xffe3: vk = 0x11; break; // XK_Control_L → VK_CONTROL
case 0xffe4: vk = 0x11; break; // XK_Control_R → VK_CONTROL
case 0xffe9: vk = 0x12; break; // XK_Alt_L → VK_MENU
case 0xffea: vk = 0x12; break; // XK_Alt_R → VK_MENU
default:
LOG_WARNING(kTag, QString("不支持的 keysym 映射: 0x%1").arg(keysym, 0, 16));
return false;
}
return simulateKeycode(vk);
#else
(void)keysym;
return false;
#endif
}
} // namespace impress

View File

@ -29,6 +29,9 @@ public:
/** @brief 模拟 keycode 按下+释放Windows 使用虚拟键码) */
bool simulateKeycode(unsigned int keycode);
/** @brief 模拟 keysym 按下+释放X11 keysym自动映射为 Windows VK 键码) */
bool simulateKeysym(unsigned long keysym);
signals:
void error(const QString& message);