diff --git a/debian/fcitx5-modules.install b/debian/fcitx5-modules.install index 108e90c76e2c0546d145a53809a461441432f478..ca9a7c29eac5dcea2347740bab09b9c98f6b2a32 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 0000000000000000000000000000000000000000..30213de042241632c03db81db4009a92e1540c8b --- /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 diff --git a/src/lib/fcitx/event.h b/src/lib/fcitx/event.h index 3ef36628a95ee8d19d7efe21779263e4fadddfa0..82c38242edf74298618ef8fc8900d4b952d4dcec 100644 --- a/src/lib/fcitx/event.h +++ b/src/lib/fcitx/event.h @@ -52,7 +52,27 @@ 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 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 d23a957d08a56bee725372e06e43cdb16bc6d527..dafe2aac71245d5e4b637f5bf627478409e4ab25 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 ace03efc2c03a990ccd95d900ac68fba017fdbf1..a807548730d4a1c2ac12282b62c48cf1d6c621d6 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 e386a5ef69e848fc3d54bce9fb660a7811bb1608..96c87b43945d72aae3e2a828562cf8b27c894d83 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 @@ -158,9 +160,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_; @@ -224,6 +226,9 @@ public: bool virtualKeyboardAutoHide_ = true; + InputMethodModeStrategy inputMethodModeStrategy_ = + InputMethodModeStrategy::AlwaysOnPhysicalKeyboard; + VirtualKeyboardFunctionMode virtualKeyboardFunctionMode_ = VirtualKeyboardFunctionMode::Full; diff --git a/src/lib/fcitx/userinterfacemanager.cpp b/src/lib/fcitx/userinterfacemanager.cpp index 19178b09a71e0d735405ab873fd1fbde68749fcf..d6a7adf9bb37ba9bd889e948ea27e806ff22e11d 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/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index 2d4ce0b8a48fd9ccf3fee9cec8e3d16218e2f62e..571df7049b2e513a8bf3b0ce5b3b6de30fb7bb86 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -17,6 +17,9 @@ add_subdirectory(unicode) add_subdirectory(clipboard) add_subdirectory(emoji) add_subdirectory(imselector) +add_subdirectory(inputmodemanager) + +add_subdirectory(keyboardstatusmanager) write_basic_package_version_file("${CMAKE_CURRENT_BINARY_DIR}/Fcitx5ModuleConfigVersion.cmake" VERSION @FCITX_VERSION@ COMPATIBILITY AnyNewerVersion) diff --git a/src/modules/inputmodemanager/CMakeLists.txt b/src/modules/inputmodemanager/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..81701a2eba85eb6730f36d43b8eec89570d99da1 --- /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 0000000000000000000000000000000000000000..9a51485e1b7dd0491a5194c2d229527fe3f86665 --- /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 0000000000000000000000000000000000000000..4766005809e5126a1dd7205c4644419576664e91 --- /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 0000000000000000000000000000000000000000..f55daa766c33a647dbc88413d3397f9e164e7cd7 --- /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 diff --git a/src/modules/keyboardstatusmanager/CMakeLists.txt b/src/modules/keyboardstatusmanager/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2fd59692ce842376a3c8b556078bb0f41814378f --- /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 0000000000000000000000000000000000000000..1b6b0bd1302e0b33f7896cd5341dda7526737c31 --- /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 0000000000000000000000000000000000000000..2e8ffa08b8a66cb932d0588511efc8badc0c4398 --- /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 0000000000000000000000000000000000000000..f4d1dfbe0b3dfaf5f4f9af8d85b61710164b73d1 --- /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 0000000000000000000000000000000000000000..8129a0cea669f169f350b1ef853b43fc85a7a8fd --- /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 0000000000000000000000000000000000000000..995b0ff217af607848904e29caa1371e4e39cb8e --- /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 diff --git a/src/ui/virtualkeyboard/virtualkeyboard.cpp b/src/ui/virtualkeyboard/virtualkeyboard.cpp index 6f3c0583089089d854bd72b46fff79baf6bcfe1d..c181d9760e87f9c897a50d0e38d7765945215298 100644 --- a/src/ui/virtualkeyboard/virtualkeyboard.cpp +++ b/src/ui/virtualkeyboard/virtualkeyboard.cpp @@ -276,7 +276,13 @@ void VirtualKeyboard::resume() { return; } - instance_->setInputMethodMode(InputMethodMode::PhysicalKeyboard); + if (instance_->inputMethodModeStrategy() != + InputMethodModeStrategy:: + OnScreenKeyboardWhenKeyboardIsDisconnect) { + return; + } + + hideVirtualKeyboard(); })); } @@ -333,7 +339,7 @@ void VirtualKeyboard::hideVirtualKeyboardForcibly() { hideVirtualKeyboard(); if (!instance_->virtualKeyboardAutoShow()) { - instance_->setInputMethodMode(InputMethodMode::PhysicalKeyboard); + instance_->setInputMethodMode(InputMethodMode::OnPhysicalKeyboard); } }