File indexing completed on 2024-10-13 10:45:59
0001 /* 0002 SPDX-FileCopyrightText: 2008 Michael Jansen <kde@michael-jansen.biz> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include <config-X11.h> 0008 0009 #include "input.h" 0010 #include "khotkeysglobal.h" 0011 #include "shortcuts_handler.h" 0012 #include "windows_handler.h" 0013 0014 // #include <X11/Xutil.h> 0015 #include <QX11Info> 0016 0017 #include <KGlobalAccel> 0018 #include <KLocalizedString> 0019 #include <QDebug> 0020 #include <QKeySequence> 0021 0022 #include <QAction> 0023 #include <QUuid> 0024 #include <kkeyserver.h> 0025 0026 namespace KHotKeys 0027 { 0028 ShortcutsHandler::ShortcutsHandler(HandlerType type, QObject *parent) 0029 : QObject(parent) 0030 , _type(type) 0031 , _actions(new KActionCollection(this, QStringLiteral("khotkeys"))) 0032 { 0033 _actions->setComponentDisplayName(i18n("Custom Shortcuts Service")); 0034 connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, &ShortcutsHandler::shortcutChanged); 0035 } 0036 0037 ShortcutsHandler::~ShortcutsHandler() 0038 { 0039 _actions->clear(); 0040 delete _actions; 0041 } 0042 0043 QAction *ShortcutsHandler::addAction(const QString &id, const QString &text, const QKeySequence &shortcut) 0044 { 0045 #ifdef KHOTKEYS_TRACE 0046 qDebug() << id << text << shortcut; 0047 #endif 0048 QString realId(id); 0049 // HACK: Do this correctly. Remove uuid on importing / exporting 0050 // On import it can happen that id is already taken. Create it under a 0051 // different name then. 0052 if (_actions->action(id)) { 0053 qDebug() << id << " already present. Using new id!"; 0054 realId = QUuid::createUuid().toString(); 0055 } 0056 0057 // Create the action 0058 QAction *newAction = _actions->addAction(realId); 0059 if (!newAction) { 0060 return nullptr; 0061 } 0062 // If our HandlerType is configuration we have to tell kdedglobalaccel 0063 // that this action is only for configuration purposes. 0064 // see KAction::~KAction 0065 if (_type == Configuration) { 0066 newAction->setProperty("isConfigurationAction", QVariant(true)); 0067 } 0068 newAction->setText(text); 0069 KGlobalAccel::self()->setShortcut(newAction, QList<QKeySequence>() << shortcut); 0070 // Enable global shortcut. If that fails there is no sense in proceeding 0071 if (!KGlobalAccel::self()->hasShortcut(newAction)) { 0072 qWarning() << "Failed to enable global shortcut for '" << text << "' " << id; 0073 _actions->removeAction(newAction); 0074 return nullptr; 0075 } 0076 Q_ASSERT(newAction->isEnabled()); 0077 0078 return newAction; 0079 } 0080 0081 QAction *ShortcutsHandler::getAction(const QString &id) 0082 { 0083 return _actions->action(id); 0084 } 0085 0086 bool ShortcutsHandler::removeAction(const QString &id) 0087 { 0088 QAction *action = getAction(id); 0089 if (!action) { 0090 return false; 0091 } else { 0092 // This will delete the action. 0093 _actions->removeAction(action); 0094 0095 return true; 0096 } 0097 } 0098 0099 #ifdef HAVE_XTEST 0100 0101 } // namespace KHotKeys 0102 #include <X11/extensions/XTest.h> 0103 namespace KHotKeys 0104 { 0105 static bool xtest_available = false; 0106 static bool xtest_inited = false; 0107 static bool xtest() 0108 { 0109 if (xtest_inited) 0110 return xtest_available; 0111 xtest_inited = true; 0112 int dummy1, dummy2, dummy3, dummy4; 0113 xtest_available = (XTestQueryExtension(QX11Info::display(), &dummy1, &dummy2, &dummy3, &dummy4) == True); 0114 return xtest_available; 0115 } 0116 0117 static void get_modifier_change(int x_mod_needed, QVector<int> &to_press, QVector<int> &to_release) 0118 { 0119 // Get state of all keys 0120 char keymap[32]; 0121 XQueryKeymap(QX11Info::display(), keymap); 0122 0123 // From KKeyServer's initializeMods() 0124 XModifierKeymap *xmk = XGetModifierMapping(QX11Info::display()); 0125 0126 for (int modidx = 0; modidx < 8; ++modidx) { 0127 bool mod_needed = x_mod_needed & (1 << modidx); 0128 for (int kcidx = 0; kcidx < xmk->max_keypermod; ++kcidx) { 0129 int keycode = xmk->modifiermap[modidx * xmk->max_keypermod + kcidx]; 0130 if (!keycode) 0131 continue; 0132 0133 bool mod_pressed = keymap[keycode / 8] & (1 << (keycode % 8)); 0134 if (mod_needed) { 0135 mod_needed = false; 0136 if (!mod_pressed) 0137 to_press.push_back(keycode); 0138 } else if (mod_pressed) 0139 to_release.push_back(keycode); 0140 } 0141 } 0142 0143 XFreeModifiermap(xmk); 0144 } 0145 0146 #endif 0147 0148 bool ShortcutsHandler::send_macro_key(const QKeySequence &key, Window window_P) 0149 { 0150 if (key.isEmpty()) 0151 return false; 0152 0153 unsigned int keysym = key[0]; 0154 int x_keycode; 0155 KKeyServer::keyQtToCodeX(keysym, &x_keycode); 0156 0157 if (x_keycode == NoSymbol) 0158 return false; 0159 0160 unsigned int x_mod; 0161 KKeyServer::keyQtToModX(keysym, &x_mod); 0162 #ifdef HAVE_XTEST 0163 if (xtest() && (window_P == None || window_P == InputFocus)) { 0164 QVector<int> keycodes_to_press, keycodes_to_release; 0165 get_modifier_change(x_mod, keycodes_to_press, keycodes_to_release); 0166 0167 for (int kc : qAsConst(keycodes_to_release)) 0168 XTestFakeKeyEvent(QX11Info::display(), kc, False, CurrentTime); 0169 0170 for (int kc : qAsConst(keycodes_to_press)) 0171 XTestFakeKeyEvent(QX11Info::display(), kc, True, CurrentTime); 0172 0173 bool ret = XTestFakeKeyEvent(QX11Info::display(), x_keycode, True, CurrentTime); 0174 ret = ret && XTestFakeKeyEvent(QX11Info::display(), x_keycode, False, CurrentTime); 0175 0176 for (int kc : qAsConst(keycodes_to_press)) 0177 XTestFakeKeyEvent(QX11Info::display(), kc, False, CurrentTime); 0178 0179 for (int kc : qAsConst(keycodes_to_release)) 0180 XTestFakeKeyEvent(QX11Info::display(), kc, True, CurrentTime); 0181 0182 return ret; 0183 } 0184 #endif 0185 if (window_P == None || window_P == InputFocus) 0186 window_P = windows_handler->active_window(); 0187 if (window_P == None) // CHECKME tohle cele je ponekud ... 0188 window_P = InputFocus; 0189 XEvent ev; 0190 ev.type = KeyPress; 0191 ev.xkey.display = QX11Info::display(); 0192 ev.xkey.window = window_P; 0193 ev.xkey.root = QX11Info::appRootWindow(); // I don't know whether these have to be set 0194 ev.xkey.subwindow = None; // to these values, but it seems to work, hmm 0195 ev.xkey.time = CurrentTime; 0196 ev.xkey.x = 0; 0197 ev.xkey.y = 0; 0198 ev.xkey.x_root = 0; 0199 ev.xkey.y_root = 0; 0200 ev.xkey.keycode = x_keycode; 0201 ev.xkey.state = x_mod; 0202 ev.xkey.same_screen = True; 0203 bool ret = XSendEvent(QX11Info::display(), window_P, True, KeyPressMask, &ev); 0204 #if 1 0205 ev.type = KeyRelease; // is this actually really needed ?? 0206 ev.xkey.display = QX11Info::display(); 0207 ev.xkey.window = window_P; 0208 ev.xkey.root = QX11Info::appRootWindow(); 0209 ev.xkey.subwindow = None; 0210 ev.xkey.time = CurrentTime; 0211 ev.xkey.x = 0; 0212 ev.xkey.y = 0; 0213 ev.xkey.x_root = 0; 0214 ev.xkey.y_root = 0; 0215 ev.xkey.state = x_mod; 0216 ev.xkey.keycode = x_keycode; 0217 ev.xkey.same_screen = True; 0218 ret = ret && XSendEvent(QX11Info::display(), window_P, True, KeyReleaseMask, &ev); 0219 #endif 0220 // Qt's autorepeat compression is broken and can create "aab" from "aba" 0221 // XSync() should create delay longer than Qt's max autorepeat interval 0222 XSync(QX11Info::display(), False); 0223 return ret; 0224 } 0225 0226 bool Mouse::send_mouse_button(int button_P, bool release_P) 0227 { 0228 #ifdef HAVE_XTEST 0229 if (xtest()) { 0230 // CHECKME tohle jeste potrebuje modifikatory 0231 // a asi i spravnou timestamp misto CurrentTime 0232 bool ret = XTestFakeButtonEvent(QX11Info::display(), button_P, True, CurrentTime); 0233 if (release_P) 0234 ret = ret && XTestFakeButtonEvent(QX11Info::display(), button_P, False, CurrentTime); 0235 return ret; 0236 } 0237 #endif 0238 return false; 0239 } 0240 0241 } // namespace KHotKeys 0242 0243 #include "moc_shortcuts_handler.cpp"