9#include "buttonrebinds_debug.h"
20#include <linux/input-event-codes.h>
38 Q_ASSERT(s_scopes > 0);
47uint RebindScope::s_scopes = 0;
54QString InputDevice::name()
const
56 return QStringLiteral(
"Button rebinding device");
59QString InputDevice::sysName()
const
64KWin::LEDs InputDevice::leds()
const
69void InputDevice::setLeds(KWin::LEDs leds)
73void InputDevice::setEnabled(
bool enabled)
77bool InputDevice::isEnabled()
const
82bool InputDevice::isKeyboard()
const
87bool InputDevice::isLidSwitch()
const
92bool InputDevice::isPointer()
const
97bool InputDevice::isTabletModeSwitch()
const
102bool InputDevice::isTabletPad()
const
107bool InputDevice::isTabletTool()
const
112bool InputDevice::isTouch()
const
117bool InputDevice::isTouchpad()
const
123 : m_configWatcher(KConfigWatcher::create(KSharedConfig::openConfig(
"kcminputrc")))
126 const QLatin1String groupName(
"ButtonRebinds");
127 connect(m_configWatcher.get(), &KConfigWatcher::configChanged,
this, [
this, groupName](
const KConfigGroup &group) {
128 if (group.parent().name() == groupName) {
129 loadConfig(group.parent());
130 }
else if (group.parent().parent().name() == groupName) {
131 loadConfig(group.parent().parent());
134 loadConfig(m_configWatcher->config()->group(groupName));
137void ButtonRebindsFilter::loadConfig(
const KConfigGroup &group)
139 Q_ASSERT(QLatin1String(
"ButtonRebinds") == group.name());
141 for (
auto &action : m_actions) {
145 bool foundActions =
false;
146 const auto mouseButtonEnum = QMetaEnum::fromType<Qt::MouseButtons>();
147 const auto mouseGroup = group.group(QStringLiteral(
"Mouse"));
148 static constexpr auto maximumQtExtraButton = 24;
149 for (
int i = 1; i <= maximumQtExtraButton; ++i) {
150 const QByteArray buttonName = QByteArray(
"ExtraButton") + QByteArray::number(i);
151 if (mouseGroup.hasKey(buttonName.constData())) {
152 const auto entry = mouseGroup.readEntry(buttonName.constData(), QStringList());
153 const auto button =
static_cast<quint32
>(mouseButtonEnum.keyToValue(buttonName));
154 insert(
Pointer, {QString(), button}, entry);
159 const auto tabletsGroup = group.group(QStringLiteral(
"Tablet"));
160 const auto tablets = tabletsGroup.groupList();
161 for (
const auto &tabletName : tablets) {
162 const auto tabletGroup = tabletsGroup.group(tabletName);
163 const auto tabletButtons = tabletGroup.keyList();
164 for (
const auto &buttonName : tabletButtons) {
165 const auto entry = tabletGroup.readEntry(buttonName, QStringList());
167 const uint button = buttonName.toUInt(&ok);
170 insert(
TabletPad, {tabletName, button}, entry);
175 const auto tabletToolsGroup = group.group(QStringLiteral(
"TabletTool"));
176 const auto tabletTools = tabletToolsGroup.groupList();
177 for (
const auto &tabletToolName : tabletTools) {
178 const auto toolGroup = tabletToolsGroup.group(tabletToolName);
179 const auto tabletToolButtons = toolGroup.keyList();
180 for (
const auto &buttonName : tabletToolButtons) {
181 const auto entry = toolGroup.readEntry(buttonName, QStringList());
183 const uint button = buttonName.toUInt(&ok);
198 if (event->type() != QEvent::MouseButtonPress && event->type() != QEvent::MouseButtonRelease) {
205 return send(
Pointer, {{},
event->button()},
event->type() == QEvent::MouseButtonPress, event->
timestamp());
213 return send(
TabletPad, {tabletPadId.
name, button}, pressed, time);
221 m_tabletTool = tabletToolId;
225void ButtonRebindsFilter::insert(TriggerType type,
const Trigger &trigger,
const QStringList &entry)
227 if (entry.size() != 2) {
228 qCWarning(KWIN_BUTTONREBINDS) <<
"Failed to rebind to" << entry;
231 if (entry.first() == QLatin1String(
"Key")) {
232 const auto keys = QKeySequence::fromString(entry.at(1), QKeySequence::PortableText);
233 if (!keys.isEmpty()) {
234 m_actions.at(type).insert(trigger, keys);
236 }
else if (entry.first() == QLatin1String(
"MouseButton")) {
238 const MouseButton mb{entry.last().toUInt(&ok)};
240 m_actions.at(type).insert(trigger, mb);
242 qCWarning(KWIN_BUTTONREBINDS) <<
"Could not convert" << entry <<
"into a mouse button";
244 }
else if (entry.first() == QLatin1String(
"TabletToolButton")) {
246 const TabletToolButton tb{entry.last().toUInt(&ok)};
248 m_actions.at(type).insert(trigger, tb);
250 qCWarning(KWIN_BUTTONREBINDS) <<
"Could not convert" << entry <<
"into a mouse button";
255bool ButtonRebindsFilter::send(TriggerType type,
const Trigger &trigger,
bool pressed, std::chrono::microseconds timestamp)
257 const auto &typeActions = m_actions.at(type);
258 if (typeActions.isEmpty()) {
262 const auto &action = typeActions[trigger];
263 if (
const QKeySequence *seq = std::get_if<QKeySequence>(&action)) {
264 return sendKeySequence(*seq, pressed, timestamp);
266 if (
const auto mb = std::get_if<MouseButton>(&action)) {
267 return sendMouseButton(mb->button, pressed, timestamp);
269 if (
const auto tb = std::get_if<TabletToolButton>(&action)) {
270 return sendTabletToolButton(tb->button, pressed, timestamp);
275static constexpr std::array<std::pair<int, int>, 4> s_modifierKeyTable = {
276 std::pair(Qt::Key_Control, KEY_LEFTCTRL),
277 std::pair(Qt::Key_Alt, KEY_LEFTALT),
278 std::pair(Qt::Key_Shift, KEY_LEFTSHIFT),
279 std::pair(Qt::Key_Meta, KEY_LEFTMETA),
282bool ButtonRebindsFilter::sendKeySequence(
const QKeySequence &keys,
bool pressed, std::chrono::microseconds time)
284 if (keys.isEmpty()) {
288 const auto &key = keys[0];
289 auto sendKey = [
this, pressed, time](xkb_keycode_t key) {
291 Q_EMIT m_inputDevice.
keyChanged(key, state, time, &m_inputDevice);
295 for (
const auto &[keySymQt, keySymLinux] : s_modifierKeyTable) {
296 if (key == keySymQt) {
298 sendKey(keySymLinux);
303 const QList<int> syms(KKeyServer::keyQtToSymXs(keys[0]));
305 qCWarning(KWIN_BUTTONREBINDS) <<
"Could not convert" << keys <<
"to keysym";
309 std::optional<int> keyCode;
310 for (
int sym : syms) {
318 qCWarning(KWIN_BUTTONREBINDS) <<
"Could not convert" << keys <<
"syms: " << syms <<
"to keycode";
324 if (key & Qt::ShiftModifier) {
325 sendKey(KEY_LEFTSHIFT);
327 if (key & Qt::ControlModifier) {
328 sendKey(KEY_LEFTCTRL);
330 if (key & Qt::AltModifier) {
331 sendKey(KEY_LEFTALT);
333 if (key & Qt::MetaModifier) {
334 sendKey(KEY_LEFTMETA);
337 sendKey(keyCode.value());
341bool ButtonRebindsFilter::sendMouseButton(quint32 button,
bool pressed, std::chrono::microseconds time)
348bool ButtonRebindsFilter::sendTabletToolButton(quint32 button,
bool pressed, std::chrono::microseconds time)
358#include "moc_buttonrebindsfilter.cpp"
std::chrono::microseconds timestamp() const
std::optional< int > keycodeFromKeysym(xkb_keysym_t keysym)
static bool isRebinding()
InputRedirection * input()