diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index edc6d45fa5..79b0e22101 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -113,6 +113,10 @@ add_subdirectory(windowview) add_subdirectory(wobblywindows) add_subdirectory(zoom) +if (KWIN_BUILD_NOTIFICATIONS) + add_subdirectory(keynotification) +endif() + if (KWIN_BUILD_DECORATIONS) add_subdirectory(kdecorations) endif() diff --git a/src/plugins/keynotification/CMakeLists.txt b/src/plugins/keynotification/CMakeLists.txt new file mode 100644 index 0000000000..3009936db7 --- /dev/null +++ b/src/plugins/keynotification/CMakeLists.txt @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2024 Nicolas Fella +# SPDX-License-Identifier: BSD-3-Clause + +kcoreaddons_add_plugin(KeyNotificationPlugin INSTALL_NAMESPACE "kwin/plugins") + +target_compile_definitions(KeyNotificationPlugin PRIVATE + -DTRANSLATION_DOMAIN=\"kwin\" +) + +target_sources(KeyNotificationPlugin PRIVATE + main.cpp + keynotification.cpp +) +target_link_libraries(KeyNotificationPlugin PRIVATE kwin KF6::Notifications KF6::I18n XKB::XKB) + diff --git a/src/plugins/keynotification/keynotification.cpp b/src/plugins/keynotification/keynotification.cpp new file mode 100644 index 0000000000..754feb0406 --- /dev/null +++ b/src/plugins/keynotification/keynotification.cpp @@ -0,0 +1,119 @@ +/* + SPDX-FileCopyrightText: 2024 Nicolas Fella + + SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL +*/ + +#include "keynotification.h" +#include "keyboard_input.h" +#include "xkb.h" + +#include +#include + +namespace KWin +{ +KeyNotificationPlugin::KeyNotificationPlugin() + : m_configWatcher(KConfigWatcher::create(KSharedConfig::openConfig("kaccessrc"))) +{ + const QLatin1String groupName("Keyboard"); + connect(m_configWatcher.get(), &KConfigWatcher::configChanged, this, [this, groupName](const KConfigGroup &group) { + if (group.name() == groupName) { + loadConfig(group); + } + }); + loadConfig(m_configWatcher->config()->group(groupName)); + + connect(input()->keyboard(), &KeyboardInputRedirection::ledsChanged, this, + &KeyNotificationPlugin::ledsChanged); + + connect(input()->keyboard()->xkb(), &Xkb::modifierStateChanged, this, &KeyNotificationPlugin::modifiersChanged); +} + +void KeyNotificationPlugin::ledsChanged(LEDs leds) +{ + if (m_enabled) { + if (!m_currentLEDs.testFlag(LED::CapsLock) && leds.testFlag(LED::CapsLock)) { + sendNotification("lockkey-locked", i18n("The Caps Lock key has been activated")); + } + + if (m_currentLEDs.testFlag(LED::CapsLock) && !leds.testFlag(LED::CapsLock)) { + sendNotification("lockkey-unlocked", i18n("The Caps Lock key is now inactive")); + } + + if (!m_currentLEDs.testFlag(LED::NumLock) && leds.testFlag(LED::NumLock)) { + sendNotification("lockkey-locked", i18n("The Num Lock key has been activated")); + } + + if (m_currentLEDs.testFlag(LED::NumLock) && !leds.testFlag(LED::NumLock)) { + sendNotification("lockkey-unlocked", i18n("The Num Lock key is now inactive")); + } + + if (!m_currentLEDs.testFlag(LED::ScrollLock) && leds.testFlag(LED::ScrollLock)) { + sendNotification("lockkey-locked", i18n("The Scroll Lock key has been activated")); + } + + if (m_currentLEDs.testFlag(LED::ScrollLock) && !leds.testFlag(LED::ScrollLock)) { + sendNotification("lockkey-unlocked", i18n("The Scroll Lock key is now inactive")); + } + } + + m_currentLEDs = leds; +} + +void KeyNotificationPlugin::modifiersChanged() +{ + Qt::KeyboardModifiers mods = input()->keyboard()->xkb()->modifiers(); + + if (m_enabled) { + if (!m_currentModifiers.testFlag(Qt::ShiftModifier) && mods.testFlag(Qt::ShiftModifier)) { + sendNotification("modifierkey-latched", i18n("The Shift key is now active.")); + } + + if (m_currentModifiers.testFlag(Qt::ShiftModifier) && !mods.testFlag(Qt::ShiftModifier)) { + sendNotification("modifierkey-unlatched", i18n("The Shift key is now inactive.")); + } + + if (!m_currentModifiers.testFlag(Qt::ControlModifier) && mods.testFlag(Qt::ControlModifier)) { + sendNotification("modifierkey-latched", i18n("The Control key is now active.")); + } + + if (m_currentModifiers.testFlag(Qt::ControlModifier) && !mods.testFlag(Qt::ControlModifier)) { + sendNotification("modifierkey-unlatched", i18n("The Control key is now inactive.")); + } + + if (!m_currentModifiers.testFlag(Qt::AltModifier) && mods.testFlag(Qt::AltModifier)) { + sendNotification("modifierkey-latched", i18n("The Alt key is now active.")); + } + + if (m_currentModifiers.testFlag(Qt::AltModifier) && !mods.testFlag(Qt::AltModifier)) { + sendNotification("modifierkey-unlatched", i18n("The Alt key is now inactive.")); + } + + if (!m_currentModifiers.testFlag(Qt::MetaModifier) && mods.testFlag(Qt::MetaModifier)) { + sendNotification("modifierkey-latched", i18n("The Meta key is now active.")); + } + + if (m_currentModifiers.testFlag(Qt::MetaModifier) && !mods.testFlag(Qt::AltModifier)) { + sendNotification("modifierkey-unlatched", i18n("The Meta key is now inactive.")); + } + } + + m_currentModifiers = input()->keyboard()->xkb()->modifiers(); +} + +void KeyNotificationPlugin::sendNotification(const QString &eventId, const QString &text) +{ + KNotification *notification = new KNotification(QStringLiteral("modifierkey-locked")); + notification->setComponentName(QStringLiteral("kaccess")); + notification->setText(text); + notification->sendEvent(); +} + +void KeyNotificationPlugin::loadConfig(const KConfigGroup &group) +{ + m_enabled = group.readEntry("kNotifyModifiers", false); +} +} + +#include "moc_keynotification.cpp" diff --git a/src/plugins/keynotification/keynotification.h b/src/plugins/keynotification/keynotification.h new file mode 100644 index 0000000000..ac619f2d65 --- /dev/null +++ b/src/plugins/keynotification/keynotification.h @@ -0,0 +1,34 @@ +/* + SPDX-FileCopyrightText: 2024 Nicolas Fella + + SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL +*/ + +#pragma once + +#include "effect/globals.h" +#include "plugin.h" + +#include +#include + +namespace KWin +{ +class KeyNotificationPlugin : public KWin::Plugin +{ + Q_OBJECT +public: + explicit KeyNotificationPlugin(); + +private: + void loadConfig(const KConfigGroup &group); + void ledsChanged(KWin::LEDs leds); + void modifiersChanged(); + void sendNotification(const QString &eventId, const QString &text); + + KConfigWatcher::Ptr m_configWatcher; + bool m_enabled = false; + KWin::LEDs m_currentLEDs; + Qt::KeyboardModifiers m_currentModifiers; +}; +} diff --git a/src/plugins/keynotification/main.cpp b/src/plugins/keynotification/main.cpp new file mode 100644 index 0000000000..2a0f7a15ba --- /dev/null +++ b/src/plugins/keynotification/main.cpp @@ -0,0 +1,32 @@ +/* + SPDX-FileCopyrightText: 2024 Nicolas Fella + + SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL +*/ + +#include "main.h" +#include "plugin.h" + +#include "keynotification.h" + +class KWIN_EXPORT KeyNotificationFactory : public KWin::PluginFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID PluginFactory_iid FILE "metadata.json") + Q_INTERFACES(KWin::PluginFactory) + +public: + std::unique_ptr create() const override + { + switch (KWin::kwinApp()->operationMode()) { + case KWin::Application::OperationModeXwayland: + case KWin::Application::OperationModeWaylandOnly: + return std::make_unique(); + case KWin::Application::OperationModeX11: + default: + return nullptr; + } + } +}; + +#include "main.moc" diff --git a/src/plugins/keynotification/metadata.json b/src/plugins/keynotification/metadata.json new file mode 100644 index 0000000000..aa304f4093 --- /dev/null +++ b/src/plugins/keynotification/metadata.json @@ -0,0 +1,5 @@ +{ + "KPlugin": { + "EnabledByDefault": true + } +}