From 2e42025134fea4fdffd734177b28b2267d4eecef Mon Sep 17 00:00:00 2001 From: hantengc Date: Mon, 28 Apr 2025 11:08:45 +0800 Subject: [PATCH 1/6] fix physicalkeyboard to OnPhysicalKeyboard,and add comment --- src/lib/fcitx/event.h | 7 ++++++- src/lib/fcitx/instance_p.h | 6 +++--- src/lib/fcitx/userinterfacemanager.cpp | 8 ++++---- src/ui/virtualkeyboard/virtualkeyboard.cpp | 4 ++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/lib/fcitx/event.h b/src/lib/fcitx/event.h index 3ef36628..1fb6caa0 100644 --- a/src/lib/fcitx/event.h +++ b/src/lib/fcitx/event.h @@ -52,7 +52,12 @@ enum class InputMethodSwitchedReason { Other, }; -enum class InputMethodMode { PhysicalKeyboard, OnScreenKeyboard }; +enum class InputMethodMode { + // On physical keyboard input mode + OnPhysicalKeyboard, + // On screen keyboard input mode + OnScreenKeyboard +}; /** * Type of input method events. diff --git a/src/lib/fcitx/instance_p.h b/src/lib/fcitx/instance_p.h index e386a5ef..7aa88a1c 100644 --- a/src/lib/fcitx/instance_p.h +++ b/src/lib/fcitx/instance_p.h @@ -158,9 +158,9 @@ public: bool exit_ = false; int exitCode_ = 0; bool running_ = false; - InputMethodMode inputMethodMode_ = isAndroid() - ? InputMethodMode::OnScreenKeyboard - : InputMethodMode::PhysicalKeyboard; + InputMethodMode inputMethodMode_ = + isAndroid() ? InputMethodMode::OnScreenKeyboard + : InputMethodMode::OnPhysicalKeyboard; EventLoop eventLoop_; EventDispatcher eventDispatcher_; diff --git a/src/lib/fcitx/userinterfacemanager.cpp b/src/lib/fcitx/userinterfacemanager.cpp index 19178b09..d6a7adf9 100644 --- a/src/lib/fcitx/userinterfacemanager.cpp +++ b/src/lib/fcitx/userinterfacemanager.cpp @@ -282,7 +282,7 @@ bool isUserInterfaceValid(UserInterface *userInterface, return (userInterface->addonInfo()->uiType() == UIType::OnScreenKeyboard && inputMethodMode == InputMethodMode::OnScreenKeyboard) || (userInterface->addonInfo()->uiType() == UIType::PhyscialKeyboard && - inputMethodMode == InputMethodMode::PhysicalKeyboard); + inputMethodMode == InputMethodMode::OnPhysicalKeyboard); } void UserInterfaceManager::updateAvailability() { @@ -294,9 +294,9 @@ void UserInterfaceManager::updateAvailability() { for (auto &name : d->uis_) { auto *ui = static_cast(d->addonManager_->addon(name, true)); - if (isUserInterfaceValid(ui, instance - ? instance->inputMethodMode() - : InputMethodMode::PhysicalKeyboard)) { + if (isUserInterfaceValid( + ui, instance ? instance->inputMethodMode() + : InputMethodMode::OnPhysicalKeyboard)) { newUI = ui; newUIName = name; break; diff --git a/src/ui/virtualkeyboard/virtualkeyboard.cpp b/src/ui/virtualkeyboard/virtualkeyboard.cpp index 6f3c0583..c46b3d57 100644 --- a/src/ui/virtualkeyboard/virtualkeyboard.cpp +++ b/src/ui/virtualkeyboard/virtualkeyboard.cpp @@ -276,7 +276,7 @@ void VirtualKeyboard::resume() { return; } - instance_->setInputMethodMode(InputMethodMode::PhysicalKeyboard); + instance_->setInputMethodMode(InputMethodMode::OnPhysicalKeyboard); })); } @@ -333,7 +333,7 @@ void VirtualKeyboard::hideVirtualKeyboardForcibly() { hideVirtualKeyboard(); if (!instance_->virtualKeyboardAutoShow()) { - instance_->setInputMethodMode(InputMethodMode::PhysicalKeyboard); + instance_->setInputMethodMode(InputMethodMode::OnPhysicalKeyboard); } } -- Gitee From d82247cda32491d24319560db9f43216dc411bfc Mon Sep 17 00:00:00 2001 From: hantengc Date: Mon, 28 Apr 2025 11:11:16 +0800 Subject: [PATCH 2/6] feat(modules):add three input mode strategies:AlwaysOnPhysicalKeyboard,AlwaysOnScreenKeyboard OnScreenKeyboardWhenKeyboardIsDisconnect. 1. AlwaysOnPhysicalKeyboard: Always using physical keyboard input mode 2. AlwaysOnScreenKeyboard: Always using screen keyboard input mode 3. OnScreenKeyboardWhenKeyboardIsDisconnect: When there is a physical keyboard,prioritize using the physical keyboard input mode.Instead,prioritize using the screen keyboard input mode.When the keyboard status changes, the input mode will also change accordingly --- src/lib/fcitx/event.h | 15 +++++++++++++++ src/lib/fcitx/instance.cpp | 10 ++++++++++ src/lib/fcitx/instance.h | 4 ++++ src/lib/fcitx/instance_p.h | 5 +++++ 4 files changed, 34 insertions(+) diff --git a/src/lib/fcitx/event.h b/src/lib/fcitx/event.h index 1fb6caa0..82c38242 100644 --- a/src/lib/fcitx/event.h +++ b/src/lib/fcitx/event.h @@ -59,6 +59,21 @@ enum class InputMethodMode { OnScreenKeyboard }; +/** + * Type of input method mode strategy. + */ +enum class InputMethodModeStrategy { + // Always using physical keyboard input mode + AlwaysOnPhysicalKeyboard, + // Always using screen keyboard input mode + AlwaysOnScreenKeyboard, + // When there is a physical keyboard, + // prioritize using the physical keyboard input mode.Instead, + // prioritize using the screen keyboard input mode.When the keyboard + // status changes, the input mode will also change accordingly + OnScreenKeyboardWhenKeyboardIsDisconnect +}; + /** * Type of input method events. */ diff --git a/src/lib/fcitx/instance.cpp b/src/lib/fcitx/instance.cpp index d23a957d..dafe2aac 100644 --- a/src/lib/fcitx/instance.cpp +++ b/src/lib/fcitx/instance.cpp @@ -1512,6 +1512,16 @@ void Instance::setVirtualKeyboardAutoHide(bool autoHide) { d->virtualKeyboardAutoHide_ = autoHide; } +InputMethodModeStrategy Instance::inputMethodModeStrategy() const { + FCITX_D(); + return d->inputMethodModeStrategy_; +} + +void Instance::setInputMethodModeStrategy(InputMethodModeStrategy strategy) { + FCITX_D(); + d->inputMethodModeStrategy_ = strategy; +} + VirtualKeyboardFunctionMode Instance::virtualKeyboardFunctionMode() const { FCITX_D(); return d->virtualKeyboardFunctionMode_; diff --git a/src/lib/fcitx/instance.h b/src/lib/fcitx/instance.h index ace03efc..a8075487 100644 --- a/src/lib/fcitx/instance.h +++ b/src/lib/fcitx/instance.h @@ -545,6 +545,10 @@ public: void setVirtualKeyboardAutoHide(bool autoHide); + InputMethodModeStrategy inputMethodModeStrategy() const; + + void setInputMethodModeStrategy(InputMethodModeStrategy strategy); + VirtualKeyboardFunctionMode virtualKeyboardFunctionMode() const; void setVirtualKeyboardFunctionMode(VirtualKeyboardFunctionMode mode); diff --git a/src/lib/fcitx/instance_p.h b/src/lib/fcitx/instance_p.h index 7aa88a1c..96c87b43 100644 --- a/src/lib/fcitx/instance_p.h +++ b/src/lib/fcitx/instance_p.h @@ -22,6 +22,8 @@ #include #include "config.h" +#include "fcitx/event.h" + #ifdef ENABLE_KEYBOARD #include #include @@ -224,6 +226,9 @@ public: bool virtualKeyboardAutoHide_ = true; + InputMethodModeStrategy inputMethodModeStrategy_ = + InputMethodModeStrategy::AlwaysOnPhysicalKeyboard; + VirtualKeyboardFunctionMode virtualKeyboardFunctionMode_ = VirtualKeyboardFunctionMode::Full; -- Gitee From ea491588eb1a645e2ca2640aca9f21cdc406ac61 Mon Sep 17 00:00:00 2001 From: hantengc Date: Thu, 8 May 2025 08:45:22 +0800 Subject: [PATCH 3/6] feat(modules):add keyboard status manager module 1. It is divided into three modules: keyboard monitor, keyboard dbus and keyboard manager. 2. The keyboard monitor can obtain the current number of keyboards and monitor the signal of the current keyboard state change. 3. Keyboard dbus allows the client to obtain the number of keyboards and sense the signal of keyboard state change. 4. The keyboard manager is responsible for managing the keyboard monitor and the keyboard dbus. --- src/modules/CMakeLists.txt | 2 + .../keyboardstatusmanager/CMakeLists.txt | 23 ++ .../fcitx5-keyboard-status.service.in | 12 + .../keyboardstatusmanager.cpp | 292 ++++++++++++++++++ .../keyboardstatusmanager.h | 143 +++++++++ .../org.fcitx.Fcitx5.KeyboardStatus.conf.in | 22 ++ ...org.fcitx.Fcitx5.KeyboardStatus.service.in | 4 + 7 files changed, 498 insertions(+) create mode 100644 src/modules/keyboardstatusmanager/CMakeLists.txt create mode 100644 src/modules/keyboardstatusmanager/fcitx5-keyboard-status.service.in create mode 100644 src/modules/keyboardstatusmanager/keyboardstatusmanager.cpp create mode 100644 src/modules/keyboardstatusmanager/keyboardstatusmanager.h create mode 100644 src/modules/keyboardstatusmanager/org.fcitx.Fcitx5.KeyboardStatus.conf.in create mode 100644 src/modules/keyboardstatusmanager/org.fcitx.Fcitx5.KeyboardStatus.service.in diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index 2d4ce0b8..e26b787e 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -18,6 +18,8 @@ add_subdirectory(clipboard) add_subdirectory(emoji) add_subdirectory(imselector) +add_subdirectory(keyboardstatusmanager) + write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/Fcitx5ModuleConfigVersion.cmake" VERSION @FCITX_VERSION@ COMPATIBILITY AnyNewerVersion) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Fcitx5ModuleConfig.cmake.in" diff --git a/src/modules/keyboardstatusmanager/CMakeLists.txt b/src/modules/keyboardstatusmanager/CMakeLists.txt new file mode 100644 index 00000000..2fd59692 --- /dev/null +++ b/src/modules/keyboardstatusmanager/CMakeLists.txt @@ -0,0 +1,23 @@ +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) + +find_package(QT NAMES Qt6 Qt5 COMPONENTS Core DBus REQUIRED) +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core DBus REQUIRED) + +pkg_check_modules(UDEV REQUIRED libudev) + +add_executable(fcitx5-keyboard-status-manager keyboardstatusmanager.cpp) +target_link_libraries(fcitx5-keyboard-status-manager Fcitx5::Utils Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::DBus ${UDEV_LIBRARIES}) +install(TARGETS fcitx5-keyboard-status-manager DESTINATION "${FCITX_INSTALL_ADDONDIR}/libexec") + +# dbus conf file +configure_file(org.fcitx.Fcitx5.KeyboardStatus.conf.in ${CMAKE_CURRENT_BINARY_DIR}/org.fcitx.Fcitx5.KeyboardStatus.conf) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.fcitx.Fcitx5.KeyboardStatus.conf DESTINATION ${CMAKE_INSTALL_PREFIX}/share/dbus-1/system.d/) + +# dbus start service file +configure_file(org.fcitx.Fcitx5.KeyboardStatus.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.fcitx.Fcitx5.KeyboardStatus.service) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.fcitx.Fcitx5.KeyboardStatus.service DESTINATION ${CMAKE_INSTALL_PREFIX}/share/dbus-1/system-services/) + +# keyboard status service +configure_file(fcitx5-keyboard-status.service.in ${CMAKE_CURRENT_BINARY_DIR}/fcitx5-keyboard-status.service) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/fcitx5-keyboard-status.service DESTINATION /lib/systemd/system/) \ No newline at end of file diff --git a/src/modules/keyboardstatusmanager/fcitx5-keyboard-status.service.in b/src/modules/keyboardstatusmanager/fcitx5-keyboard-status.service.in new file mode 100644 index 00000000..1b6b0bd1 --- /dev/null +++ b/src/modules/keyboardstatusmanager/fcitx5-keyboard-status.service.in @@ -0,0 +1,12 @@ +[Unit] +Description=Fcitx5 Keyboard Status Manager + +[Service] +Type=simple +User=root +ExecStart=${FCITX_INSTALL_ADDONDIR}/libexec/fcitx5-keyboard-status-manager +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/src/modules/keyboardstatusmanager/keyboardstatusmanager.cpp b/src/modules/keyboardstatusmanager/keyboardstatusmanager.cpp new file mode 100644 index 00000000..2e8ffa08 --- /dev/null +++ b/src/modules/keyboardstatusmanager/keyboardstatusmanager.cpp @@ -0,0 +1,292 @@ +/* + * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + */ +#include "keyboardstatusmanager.h" + +#include +#include +#include +#include +#include + +#define PROCBUSINPUTDEVICES "/proc/bus/input/devices" +#define DEVICEINFO_EV_KBD "EV=120013" + +const QString m_serviceName = "org.fcitx.Fcitx5.KeyboardStatus"; + +namespace fcitx { + +KeyboardStatusMonitor::KeyboardStatusMonitor(QObject *parent) + : QObject(parent) { + initSignalTimer(); + updateProcInputDevices(); + setupUdevMonitoring(); +} + +KeyboardStatusMonitor::~KeyboardStatusMonitor() { + if (m_monitor != nullptr) { + udev_monitor_unref(m_monitor); + m_monitor = nullptr; + } + + if (m_udev != nullptr) { + udev_unref(m_udev); + m_udev = nullptr; + } +} + +void KeyboardStatusMonitor::updateProcInputDevices() { + std::cout << "func : " << __FUNCTION__ << std::endl; + + const auto content = readFile(PROCBUSINPUTDEVICES); + if (content.isEmpty()) { + return; + } + + m_deviceInfoMap.clear(); + const auto deviceBlocks = splitIntoDeviceBlocks(content); + parseAllDeviceBlocks(deviceBlocks); +} + +void KeyboardStatusMonitor::initSignalTimer() { + std::cout << "func : " << __FUNCTION__ << std::endl; + m_signalMergingTimer = new QTimer(this); + m_signalMergingTimer->setSingleShot(true); + connect(m_signalMergingTimer, &QTimer::timeout, this, + &KeyboardStatusMonitor::onFileChanged); +} + +void KeyboardStatusMonitor::onFileChanged() { + updateProcInputDevices(); + Q_EMIT keyboardStatusChanged(); +} + +void KeyboardStatusMonitor::addDeviceToMap(const QString &id, + const DeviceInfo &info) { + if (id.isEmpty()) { + return; + } + + if (m_deviceInfoMap.keys().contains(id)) { + m_deviceInfoMap[id].append(info); + } else { + QList devInfoList = {info}; + m_deviceInfoMap.insert(id, devInfoList); + } + + std::cout << "add device to map, map size is :" << m_deviceInfoMap.size() + << std::endl; +} + +void KeyboardStatusMonitor::setupUdevMonitoring() { + std::cout << "func : " << __FUNCTION__ << std::endl; + m_udev = udev_new(); + if (m_udev == nullptr) { + std::cerr << "func : " << __FUNCTION__ << "Failed to new udev" + << std::endl; + return; + } + + m_monitor = udev_monitor_new_from_netlink(m_udev, "udev"); + if (m_monitor == nullptr) { + std::cerr << "func : " << __FUNCTION__ << "Failed to new udev monitor" + << std::endl; + return; + } + + udev_monitor_filter_add_match_subsystem_devtype(m_monitor, "input", + nullptr); + udev_monitor_enable_receiving(m_monitor); + + setupSocketNotifier(udev_monitor_get_fd(m_monitor)); +} + +void KeyboardStatusMonitor::setupSocketNotifier(int fd) { + std::cout << "func : " << __FUNCTION__ << " fd : " << fd << std::endl; + auto notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(notifier, &QSocketNotifier::activated, this, [this] { + udev_device *dev = udev_monitor_receive_device(m_monitor); + if (dev != nullptr) { + m_signalMergingTimer->start(1000); + udev_device_unref(dev); + } + }); +} + +const QString KeyboardStatusMonitor::readFile(const QString &path) const { + std::cout << "func : " << __FUNCTION__ << std::endl; + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + std::cerr << "func : " << __FUNCTION__ << "Failed to open file" + << std::endl; + return ""; + } + return QString::fromLocal8Bit(file.readAll()); +} + +QList +KeyboardStatusMonitor::splitIntoDeviceBlocks(const QString &content) const { + std::cout << "func : " << __FUNCTION__ << std::endl; + + QList blocks; + QStringList currentBlock; + + for (const auto &line : content.split('\n')) { + if (line.trimmed().isEmpty()) { + if (!currentBlock.isEmpty()) { + blocks.append(currentBlock); + currentBlock.clear(); + } + } else { + currentBlock.append(line); + } + } + if (!currentBlock.isEmpty()) { + blocks.append(currentBlock); + } + + return blocks; +} + +void KeyboardStatusMonitor::parseAllDeviceBlocks( + const QList &blocks) { + std::cout << "func : " << __FUNCTION__; + + for (const auto &block : blocks) { + auto [id, info] = parseSingleDeviceBlock(block); + std::cout << "id : " << id.toStdString() << std::endl; + std::cout << " device info name : " << info.name.toStdString() + << std::endl; + std::cout << " device info eventcap : " + << info.eventCapabilities.toStdString() << std::endl; + addDeviceToMap(id, info); + } +} + +std::pair +KeyboardStatusMonitor::parseSingleDeviceBlock(const QStringList &block) const { + QString id; + DeviceInfo info; + + for (const auto &line : block) { + parseDeviceLine(line, id, info); + } + + return {id, info}; +} + +void KeyboardStatusMonitor::parseDeviceLine(const QString &line, QString &outId, + DeviceInfo &outInfo) const { + // "X: "格式前缀长度 + constexpr auto PREFIX_LEN = 3; + + if (line.startsWith("I: ")) { + outId = line.mid(PREFIX_LEN).trimmed(); + } else if (line.startsWith("N: ")) { + outInfo.name = line.mid(PREFIX_LEN).trimmed(); + } else if (line.startsWith("P: ")) { + outInfo.physPath = line.mid(PREFIX_LEN).trimmed(); + } else if (line.startsWith("S: ")) { + outInfo.sysfsPath = line.mid(PREFIX_LEN).trimmed(); + } else if (line.startsWith("B: ") && line.contains("EV=")) { + outInfo.eventCapabilities = line.mid(PREFIX_LEN).trimmed(); + } +} + +int KeyboardStatusMonitor::getKeyboardNum() { + int keyboardNum = 0; + + if (m_deviceInfoMap.isEmpty()) { + std::cerr << "m_deviceInfoMap is empty."; + return keyboardNum; + } + + for (const auto &list : m_deviceInfoMap) { + if (list.first().eventCapabilities.contains(DEVICEINFO_EV_KBD)) { + keyboardNum++; + } + } + + std::cout << "func: " << __FUNCTION__ << " keyboardNum: " << keyboardNum + << std::endl; + return keyboardNum; +} + +KeyboardStatusManager::KeyboardStatusManager(QObject *parent) + : QObject(parent), + m_keyboardStatusMonitor(new KeyboardStatusMonitor(parent)), + m_keyboardStatusDBusService( + new KeyboardStatusDBusService(parent, m_keyboardStatusMonitor)) { + + registerDBus(); + + QObject::connect( + m_keyboardStatusMonitor, &KeyboardStatusMonitor::keyboardStatusChanged, + this, [this]() { + if (m_keyboardStatusDBusService == nullptr) { + return; + } + + emit m_keyboardStatusDBusService->keyboardStatusChanged(); + }); +} + +KeyboardStatusManager::~KeyboardStatusManager() { + if (m_keyboardStatusDBusService) { + delete m_keyboardStatusDBusService; + m_keyboardStatusDBusService = nullptr; + } + + if (m_keyboardStatusMonitor) { + delete m_keyboardStatusMonitor; + m_keyboardStatusMonitor = nullptr; + } +} + +void KeyboardStatusManager::registerDBus() { + + QDBusConnection systemBus = QDBusConnection::systemBus(); + if (!systemBus.isConnected()) { + std::cerr << "Failed to connect to system dbus. error message:" + << systemBus.lastError().message().toStdString(); + return; + } + + // 查看dbus服务是否已经注册 + if (systemBus.interface()->isServiceRegistered(m_serviceName)) { + std::cerr << "This service has already been registered. error message:" + << systemBus.lastError().message().toStdString(); + return; + } + + // 注册服务名称 + if (!systemBus.registerService(m_serviceName)) { + std::cerr << "Service registration failed. error message:" + << systemBus.lastError().message().toStdString(); + return; + } + + // 注册对象路径 + if (!systemBus.registerObject("/", m_keyboardStatusDBusService, + QDBusConnection::ExportAllSlots | + QDBusConnection::ExportAllSignals)) { + systemBus.unregisterService(m_serviceName); + std::cerr << "Failed to register dbus service path. error message:" + << systemBus.lastError().message().toStdString(); + return; + } + + std::cout << "register service successful." << std::endl; +} +} // namespace fcitx + +int main(int argc, char *argv[]) { + QCoreApplication app(argc, argv); + + fcitx::KeyboardStatusManager keyboardManager; + + return app.exec(); +} diff --git a/src/modules/keyboardstatusmanager/keyboardstatusmanager.h b/src/modules/keyboardstatusmanager/keyboardstatusmanager.h new file mode 100644 index 00000000..f4d1dfbe --- /dev/null +++ b/src/modules/keyboardstatusmanager/keyboardstatusmanager.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + */ +#ifndef _KEYBOARDSTATUSMANAGER_H_ +#define _KEYBOARDSTATUSMANAGER_H_ + +#include +#include + +#include +#include +#include +#include + +#include + +namespace fcitx { + +/** + * Obtain keyboard status and monitor signals of keyboard status changes. + * 1. Provide an interface to retrieve keyboard status. + * 2. Monitor the signal of keyboard status changes and then send out a signal. + */ +class KeyboardStatusMonitor : public QObject { + Q_OBJECT +public: + struct DeviceInfo { + QString name; + QString physPath; + QString sysfsPath; + QString eventCapabilities; + }; + + explicit KeyboardStatusMonitor(QObject *parent = nullptr); + ~KeyboardStatusMonitor() override; + + /** + * 获取键盘数量 + */ + int getKeyboardNum(); + +private: + /** + * 初始化定时器 + * 在检测到文件发生变化后被触发,进而更新键盘状态 + */ + void initSignalTimer(); + + /** + * 通过Udev监控输入设备的变化 + */ + void setupUdevMonitoring(); + + /** + * 通过文件描述符查询设备状态 + */ + void setupSocketNotifier(int fd); + + /** + * 添加设备到列表中 + */ + void addDeviceToMap(const QString &id, const DeviceInfo &info); + + /** + * 更新键盘设备状态 + */ + void updateProcInputDevices(); + + void parseAllDeviceBlocks(const QList &blocks); + QList splitIntoDeviceBlocks(const QString &content) const; + const QString readFile(const QString &path) const; + + void parseDeviceLine(const QString &line, QString &outId, + DeviceInfo &outInfo) const; + + std::pair + parseSingleDeviceBlock(const QStringList &block) const; + +private: + QMap> m_deviceInfoMap; + QTimer *m_signalMergingTimer = nullptr; + struct udev *m_udev = nullptr; + struct udev_monitor *m_monitor = nullptr; + QSocketNotifier *m_notifier = nullptr; + +private Q_SLOTS: + void onFileChanged(); + +signals: + void keyboardStatusChanged(); +}; + +/** + * As a dbus service, users can obtain keyboard numbers and listen for signals + * indicating changes in keyboard status through this service + */ +class KeyboardStatusDBusService : public QObject { + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.fcitx.Fcitx5.KeyboardStatus") +public: + KeyboardStatusDBusService(QObject *parent, + KeyboardStatusMonitor *keyboardStatusMonitor) + : QObject(parent), m_keyboardStatusMonitor(keyboardStatusMonitor) {} + +public Q_SLOTS: + int getKeyboardNum() { + std::cout << "func : " << __FUNCTION__ << std::endl; + if (m_keyboardStatusMonitor == nullptr) { + // 默认存在键盘,确保不会转换到屏幕键盘输入模式 + std::cerr << "m_keyboardStatusMonitor is nullptr,return."; + return 1; + } + + return m_keyboardStatusMonitor->getKeyboardNum(); + } + +signals: + void keyboardStatusChanged(); + +private: + KeyboardStatusMonitor *m_keyboardStatusMonitor = nullptr; +}; + +/** + * Manage keyboard status monitor and keyboard status dbus service + */ +class KeyboardStatusManager : public QObject { +public: + explicit KeyboardStatusManager(QObject *parent = nullptr); + ~KeyboardStatusManager() override; + +private: + void registerDBus(); + +private: + KeyboardStatusMonitor *m_keyboardStatusMonitor = nullptr; + KeyboardStatusDBusService *m_keyboardStatusDBusService = nullptr; +}; +} // namespace fcitx +#endif \ No newline at end of file diff --git a/src/modules/keyboardstatusmanager/org.fcitx.Fcitx5.KeyboardStatus.conf.in b/src/modules/keyboardstatusmanager/org.fcitx.Fcitx5.KeyboardStatus.conf.in new file mode 100644 index 00000000..8129a0ce --- /dev/null +++ b/src/modules/keyboardstatusmanager/org.fcitx.Fcitx5.KeyboardStatus.conf.in @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + diff --git a/src/modules/keyboardstatusmanager/org.fcitx.Fcitx5.KeyboardStatus.service.in b/src/modules/keyboardstatusmanager/org.fcitx.Fcitx5.KeyboardStatus.service.in new file mode 100644 index 00000000..995b0ff2 --- /dev/null +++ b/src/modules/keyboardstatusmanager/org.fcitx.Fcitx5.KeyboardStatus.service.in @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.fcitx.Fcitx5.KeyboardStatus +Exec=${FCITX_INSTALL_ADDONDIR}/libexec/fcitx5-keyboard-status-manager +User=root \ No newline at end of file -- Gitee From 7763098aa545f94be1ff7871a5cf3c7b386f2d29 Mon Sep 17 00:00:00 2001 From: hantengc Date: Fri, 25 Apr 2025 14:22:57 +0800 Subject: [PATCH 4/6] feat(modules):add input mode manager --- src/modules/CMakeLists.txt | 1 + src/modules/inputmodemanager/CMakeLists.txt | 9 + .../inputmodemanager.conf.in.in | 13 ++ .../inputmodemanager/inputmodemanager.cpp | 157 ++++++++++++++++++ .../inputmodemanager/inputmodemanager.h | 80 +++++++++ 5 files changed, 260 insertions(+) create mode 100644 src/modules/inputmodemanager/CMakeLists.txt create mode 100644 src/modules/inputmodemanager/inputmodemanager.conf.in.in create mode 100644 src/modules/inputmodemanager/inputmodemanager.cpp create mode 100644 src/modules/inputmodemanager/inputmodemanager.h diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index e26b787e..571df704 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -17,6 +17,7 @@ add_subdirectory(unicode) add_subdirectory(clipboard) add_subdirectory(emoji) add_subdirectory(imselector) +add_subdirectory(inputmodemanager) add_subdirectory(keyboardstatusmanager) diff --git a/src/modules/inputmodemanager/CMakeLists.txt b/src/modules/inputmodemanager/CMakeLists.txt new file mode 100644 index 00000000..81701a2e --- /dev/null +++ b/src/modules/inputmodemanager/CMakeLists.txt @@ -0,0 +1,9 @@ +add_fcitx5_addon(inputmodemanager inputmodemanager.cpp) + +target_link_libraries(inputmodemanager Fcitx5::Core Fcitx5::Module::DBus) +install(TARGETS inputmodemanager DESTINATION ${FCITX_INSTALL_ADDONDIR}) + +configure_file(inputmodemanager.conf.in.in inputmodemanager.conf.in @ONLY) +fcitx5_translate_desktop_file(${CMAKE_CURRENT_BINARY_DIR}/inputmodemanager.conf.in inputmodemanager.conf) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/inputmodemanager.conf DESTINATION ${FCITX_INSTALL_PKGDATADIR}/addon COMPONENT config) \ No newline at end of file diff --git a/src/modules/inputmodemanager/inputmodemanager.conf.in.in b/src/modules/inputmodemanager/inputmodemanager.conf.in.in new file mode 100644 index 00000000..9a51485e --- /dev/null +++ b/src/modules/inputmodemanager/inputmodemanager.conf.in.in @@ -0,0 +1,13 @@ +[Addon] +Name=Input Mode Manager +Comment=input mode manager for classic ui and virtual keyboard +Type=@FCITX_ADDON_TYPE@ +Library=libinputmodemanager +Category=Module +Version=@PROJECT_VERSION@ +OnDemand=False +Configurable=True + +[Addon/Dependencies] +0=core:@PROJECT_VERSION@ +1=dbus diff --git a/src/modules/inputmodemanager/inputmodemanager.cpp b/src/modules/inputmodemanager/inputmodemanager.cpp new file mode 100644 index 00000000..47660058 --- /dev/null +++ b/src/modules/inputmodemanager/inputmodemanager.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + */ +#include "inputmodemanager.h" + +#include "fcitx-utils/dbus/bus.h" +#include "fcitx-utils/dbus/message.h" +#include "fcitx-utils/dbus/objectvtable.h" +#include "dbus_public.h" + +namespace fcitx { + +static const char KeyboardManagerServiceName[] = + "org.fcitx.Fcitx5.KeyboardStatus"; +static const char KeyboardManagerInterfaceName[] = + "org.fcitx.Fcitx5.KeyboardStatus"; + +InputModeManager::InputModeManager(Instance *instance) + : instance_(instance), bus_(new dbus::Bus(dbus::BusType::System)), + watcher_(new dbus::ServiceWatcher(*bus_)) { + bus_->attachEventLoop(&instance->eventLoop()); + + reloadConfig(); + updateInputModeStrategy(); + connectKeyboardSignals(); +} + +void InputModeManager::updateInputModeStrategy() { + switch (inputModeConfig_.inputModeStrategySelect.value()) { + case InputMethodModeStrategy::AlwaysOnPhysicalKeyboard: { + FCITX_INFO() << "updateInputModeStrategy : AlwaysOnPhysicalKeyboard"; + instance_->setInputMethodModeStrategy( + InputMethodModeStrategy::AlwaysOnPhysicalKeyboard); + instance_->setInputMethodMode(InputMethodMode::OnPhysicalKeyboard); + break; + } + case InputMethodModeStrategy::AlwaysOnScreenKeyboard: { + FCITX_INFO() << "updateInputModeStrategy : AlwaysOnScreenKeyboard"; + + instance_->setInputMethodModeStrategy( + InputMethodModeStrategy::AlwaysOnScreenKeyboard); + instance_->setInputMethodMode(InputMethodMode::OnScreenKeyboard); + break; + } + case InputMethodModeStrategy::OnScreenKeyboardWhenKeyboardIsDisconnect: { + FCITX_INFO() << "updateInputModeStrategy : " + "OnScreenKeyboardWhenKeyboardIsDisconnect"; + instance_->setInputMethodModeStrategy( + InputMethodModeStrategy::OnScreenKeyboardWhenKeyboardIsDisconnect); + updateInputMode(); + break; + } + default: + FCITX_INFO() << "please reset input mode!"; + break; + } +} + +void InputModeManager::updateInputMode() { + int num = callDBusToGetKeyboardStatus(); + if (num > 0) { + FCITX_INFO() << "update input mode : OnPhysicalKeyboard"; + instance_->setInputMethodMode(InputMethodMode::OnPhysicalKeyboard); + } else { + FCITX_INFO() << "update input mode : OnScreenKeyboard"; + instance_->setInputMethodMode(InputMethodMode::OnScreenKeyboard); + } +} + +void InputModeManager::connectKeyboardSignals() { + entry_ = watcher_->watchService( + KeyboardManagerServiceName, + [this](const std::string &service, const std::string &, + const std::string &newName) { + FCITX_INFO() << "KeyboardManager service name : " << service + << ", new name : " << newName; + if (newName.empty()) { + return; + } + + if (instance_->inputMethodModeStrategy() != + InputMethodModeStrategy:: + OnScreenKeyboardWhenKeyboardIsDisconnect) { + return; + } + + updateInputMode(); + }); + + slot_ = bus_->addMatch( + dbus::MatchRule(KeyboardManagerServiceName, "/", + KeyboardManagerInterfaceName, "keyboardStatusChanged"), + [this](dbus::Message &message) { + FCITX_UNUSED(message); + + FCITX_INFO() << "keyboardStatusChanged "; + if (instance_->inputMethodModeStrategy() != + InputMethodModeStrategy:: + OnScreenKeyboardWhenKeyboardIsDisconnect) { + FCITX_INFO() << "keyboardStatusChanged,but current " + "inputMethodModeStrategy is not " + "OnScreenKeyboardWhenKeyboardIsDisconnect."; + return true; + } + + updateInputMode(); + return true; + }); +} + +int InputModeManager::callDBusToGetKeyboardStatus() { + auto msg = + bus_->createMethodCall(KeyboardManagerServiceName, "/", + KeyboardManagerInterfaceName, "getKeyboardNum"); + if (msg.isError()) { + FCITX_INFO() << "message is error, keyboardNum set default value : 1"; + return keyboardNum; + } + + auto replay = msg.call(0); + if (replay.isError()) { + FCITX_INFO() << "reply is error, keyboardNum set default value : 1"; + return keyboardNum; + } + + replay >> keyboardNum; + + FCITX_INFO() << "keyboardNum : " << keyboardNum; + + return keyboardNum; +} + +const Configuration *InputModeManager::getConfig() const { + return &inputModeConfig_; +} + +void InputModeManager::reloadConfig() { + readAsIni(inputModeConfig_, "conf/inputmode.conf"); +} + +void InputModeManager::setConfig(const RawConfig &config) { + inputModeConfig_.load(config, true); + safeSaveAsIni(inputModeConfig_, "conf/inputmode.conf"); + updateInputModeStrategy(); +} + +class InputModeFactory : public AddonFactory { + AddonInstance *create(AddonManager *manager) override { + return new InputModeManager(manager->instance()); + } +}; +} // namespace fcitx + +FCITX_ADDON_FACTORY_V2(inputmodemanager, fcitx::InputModeFactory); diff --git a/src/modules/inputmodemanager/inputmodemanager.h b/src/modules/inputmodemanager/inputmodemanager.h new file mode 100644 index 00000000..f55daa76 --- /dev/null +++ b/src/modules/inputmodemanager/inputmodemanager.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + */ +#ifndef _FCITX_MODULES_INPUTMODE_H_ +#define _FCITX_MODULES_INPUTMODE_H_ + +#include "fcitx-config/iniparser.h" +#include "fcitx-utils/dbus/servicewatcher.h" +#include "fcitx-utils/i18n.h" +#include "fcitx/addonfactory.h" +#include "fcitx/addoninstance.h" +#include "fcitx/addonmanager.h" +#include "fcitx/event.h" +#include "fcitx/instance.h" +#include "fcitx/userinterfacemanager.h" + +namespace fcitx { + +/** + * Implement configurable functions on the configuration interface + */ +FCITX_CONFIG_ENUM_NAME_WITH_I18N( + InputMethodModeStrategy, N_("Always On Physical Keyboard"), + N_("Always On Screen Keyboard"), + N_("On Screen Keyboard When Keyboard Is Disconnection")) + +/** + * Default configuration :OnScreenKeyboardWhenKeyboardIsDisconnect + */ +FCITX_CONFIGURATION( + InputModeConfig, + OptionWithAnnotation + inputModeStrategySelect{ + this, "InputMethodMode", _("select input mode strategy"), + InputMethodModeStrategy::OnScreenKeyboardWhenKeyboardIsDisconnect};) + +class InputModeManager : public AddonInstance { +public: + explicit InputModeManager(Instance *instance); + ~InputModeManager() override = default; + +protected: + /** + * Save input mode + */ + void reloadConfig() override; + const Configuration *getConfig() const override; + void setConfig(const RawConfig &config) override; + +private: + /** + * Update input mode based on configuration file. + */ + void updateInputModeStrategy(); + + /** + * Update input mode based on keyboard status. + */ + void updateInputMode(); + + void connectKeyboardSignals(); + int callDBusToGetKeyboardStatus(); + + FCITX_ADDON_DEPENDENCY_LOADER(dbus, instance_->addonManager()); + +private: + Instance *instance_; + InputModeConfig inputModeConfig_; + dbus::Bus *bus_ = nullptr; + dbus::ServiceWatcher *watcher_ = nullptr; + std::unique_ptr entry_; + std::unique_ptr slot_; + int keyboardNum = 1; +}; +} // namespace fcitx +#endif \ No newline at end of file -- Gitee From eae731e533111ec9af2ac678e12bdbb3b1748980 Mon Sep 17 00:00:00 2001 From: hantengc Date: Mon, 28 Apr 2025 15:18:35 +0800 Subject: [PATCH 5/6] fix:Modify the virtual keyboard logic to support three inputmethod mode strategies. --- src/ui/virtualkeyboard/virtualkeyboard.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ui/virtualkeyboard/virtualkeyboard.cpp b/src/ui/virtualkeyboard/virtualkeyboard.cpp index c46b3d57..c181d976 100644 --- a/src/ui/virtualkeyboard/virtualkeyboard.cpp +++ b/src/ui/virtualkeyboard/virtualkeyboard.cpp @@ -276,7 +276,13 @@ void VirtualKeyboard::resume() { return; } - instance_->setInputMethodMode(InputMethodMode::OnPhysicalKeyboard); + if (instance_->inputMethodModeStrategy() != + InputMethodModeStrategy:: + OnScreenKeyboardWhenKeyboardIsDisconnect) { + return; + } + + hideVirtualKeyboard(); })); } -- Gitee From 99cfc7c0a1bd9e04f68a41f6a9dfa369eb9bec1a Mon Sep 17 00:00:00 2001 From: hantengc Date: Fri, 9 May 2025 14:40:46 +0800 Subject: [PATCH 6/6] fix:Compliant with deb packaging specifications --- debian/fcitx5-modules.install | 5 +++++ debian/fcitx5-modules.postinst | 7 +++++++ 2 files changed, 12 insertions(+) create mode 100644 debian/fcitx5-modules.postinst diff --git a/debian/fcitx5-modules.install b/debian/fcitx5-modules.install index 108e90c7..ca9a7c29 100644 --- a/debian/fcitx5-modules.install +++ b/debian/fcitx5-modules.install @@ -21,7 +21,10 @@ usr/lib/*/fcitx5/libwayland.so usr/lib/*/fcitx5/libwaylandim.so usr/lib/*/fcitx5/libxcb.so usr/lib/*/fcitx5/libxim.so +usr/lib/*/fcitx5/libinputmodemanager.so usr/share/dbus-1/services/org.fcitx.Fcitx5.service +usr/share/dbus-1/system-services/org.fcitx.Fcitx5.KeyboardStatus.service +usr/share/dbus-1/system.d/org.fcitx.Fcitx5.KeyboardStatus.conf usr/share/fcitx5/addon/classicui.conf usr/share/fcitx5/addon/clipboard.conf usr/share/fcitx5/addon/dbus.conf @@ -42,7 +45,9 @@ usr/share/fcitx5/addon/wayland.conf usr/share/fcitx5/addon/waylandim.conf usr/share/fcitx5/addon/xcb.conf usr/share/fcitx5/addon/xim.conf +usr/share/fcitx5/addon/inputmodemanager.conf usr/share/fcitx5/data/quickphrase.d usr/share/fcitx5/testing/addon/testfrontend.conf usr/share/fcitx5/testing/addon/testim.conf usr/share/fcitx5/testing/addon/testui.conf +lib/systemd/system/fcitx5-keyboard-status.service diff --git a/debian/fcitx5-modules.postinst b/debian/fcitx5-modules.postinst new file mode 100644 index 00000000..30213de0 --- /dev/null +++ b/debian/fcitx5-modules.postinst @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +systemctl reload dbus.service >/dev/null 2>&1 + +exit 0 -- Gitee