diff --git a/daemon/core.cpp b/daemon/core.cpp index ce2240f..8aa0edd 100644 --- a/daemon/core.cpp +++ b/daemon/core.cpp @@ -37,7 +37,6 @@ #include #include -#include #include #include #include @@ -48,7 +47,6 @@ #include -#include "pipe_utils.h" #include "string_utils.h" #include "daemon_adaptor.h" #include "native_adaptor.h" @@ -343,46 +341,9 @@ const char *x11opcodeToString(unsigned char opcode) return ""; } -static const char *strLevel(int level) -{ - switch (level) - { - case LOG_EMERG: - return "Emergency"; - - case LOG_ALERT: - return "Alert"; - - case LOG_CRIT: - return "Critical"; - - case LOG_ERR: - return "Error"; - - case LOG_WARNING: - return "Warning"; - - case LOG_NOTICE: - return "Notice"; - - case LOG_INFO: - return "Info"; - - case LOG_DEBUG: - return "Debug"; - - default: - return ""; - } -} - - Core::Core(bool useSyslog, bool minLogLevelSet, int minLogLevel, const QStringList &configFiles, bool multipleActionsBehaviourSet, MultipleActionsBehaviour multipleActionsBehaviour, QObject *parent) : QThread(parent) - , LogTarget() , mReady(false) - , mUseSyslog(useSyslog) - , mMinLogLevel(minLogLevel) , mDisplay(nullptr) , mInterClientCommunicationWindow(0) , mServiceWatcher{new QDBusServiceWatcher{this}} @@ -401,6 +362,15 @@ Core::Core(bool useSyslog, bool minLogLevelSet, int minLogLevel, const QStringLi , mShortcutGrabTimeout(new QTimer(this)) , mShortcutGrabRequested(false) { +#if 0 + // debugging + Q_UNUSED(minLogLevel); + Q_UNUSED(useSyslog); + mCoreLogger = std::make_unique(LOG_DEBUG, false); +#else + mCoreLogger = std::make_unique(minLogLevel, useSyslog); +#endif + s_Core = this; initBothPipeEnds(mX11ErrorPipe); @@ -481,33 +451,16 @@ Core::Core(bool useSyslog, bool minLogLevelSet, int minLogLevel, const QStringLi if (!minLogLevelSet) { iniValue = settings.value(/* General/ */QStringLiteral("LogLevel")).toString(); - if (!iniValue.isEmpty()) + auto lvl = LogTarget::levelFromStr(qPrintable(iniValue)); + if (lvl >= 0) { + mCoreLogger->mMinLogLevel = lvl; minLogLevelSet = true; - if (iniValue == QLatin1String("error")) - { - mMinLogLevel = LOG_ERR; - } - else if (iniValue == QLatin1String("warning")) - { - mMinLogLevel = LOG_WARNING; - } - else if (iniValue == QLatin1String("notice")) - { - mMinLogLevel = LOG_NOTICE; - } - else if (iniValue == QLatin1String("info")) - { - mMinLogLevel = LOG_INFO; - } - else if (iniValue == QLatin1String("debug")) - { - mMinLogLevel = LOG_DEBUG; - } - else - { - minLogLevelSet = false; - } + } + else + { + auto lvlStr = LogTarget::strLevel(mCoreLogger->mMinLogLevel); + qInfo("no loglevel configured in %s (result = %d); current loglevel: %s", qUtf8Printable(settings.fileName()), lvl, lvlStr); } } @@ -675,7 +628,7 @@ Core::Core(bool useSyslog, bool minLogLevelSet, int minLogLevel, const QStringLi log(LOG_DEBUG, "Config file: %s", qPrintable(configs[0])); } - log(LOG_DEBUG, "MinLogLevel: %s", strLevel(mMinLogLevel)); + log(LOG_DEBUG, "MinLogLevel: %s", LogTarget::strLevel(mCoreLogger->mMinLogLevel)); switch (mMultipleActionsBehaviour) { case MULTIPLE_ACTIONS_BEHAVIOUR_FIRST: @@ -694,8 +647,7 @@ Core::Core(bool useSyslog, bool minLogLevelSet, int minLogLevel, const QStringLi log(LOG_DEBUG, "MultipleActionsBehaviour: none"); break; - default: - ; + case MULTIPLE_ACTIONS_BEHAVIOUR__COUNT: break; // just a counter } log(LOG_DEBUG, "AllowGrabLocks: %s", mAllowGrabLocks ? "true" : "false"); log(LOG_DEBUG, "AllowGrabBaseSpecial: %s", mAllowGrabBaseSpecial ? "true" : "false"); @@ -850,12 +802,12 @@ void Core::saveConfig() if (!strcmp(action->type(), CommandAction::id())) { - const CommandAction *commandAction = dynamic_cast(action); + auto commandAction = static_cast(action); settings.setValue(ExecKey, QVariant(QStringList() << commandAction->command() += commandAction->args())); } else if (!strcmp(action->type(), MethodAction::id())) { - const MethodAction *methodAction = dynamic_cast(action); + auto methodAction = static_cast(action); settings.setValue(serviceKey, methodAction->service()); settings.setValue(pathKey, methodAction->path().path()); settings.setValue(interfaceKey, methodAction->interface()); @@ -863,7 +815,7 @@ void Core::saveConfig() } else if (!strcmp(action->type(), ClientAction::id())) { - const ClientAction *clientAction = dynamic_cast(action); + auto clientAction = static_cast(action); settings.setValue(pathKey, clientAction->path().path()); } @@ -877,28 +829,6 @@ void Core::unixSignalHandler(int signalNumber) qApp->quit(); } -void Core::log(int level, const char *format, ...) const -{ - if (level > mMinLogLevel) - { - return; - } - - va_list ap; - va_start(ap, format); - if (mUseSyslog) - { - vsyslog(LOG_MAKEPRI(LOG_USER, level), format, ap); - } - else - { - fprintf(stderr, "[%s] ", strLevel(level)); - vfprintf(stderr, format, ap); - fprintf(stderr, "\n"); - } - va_end(ap); -} - int Core::x11ErrorHandler(Display */*display*/, XErrorEvent *errorEvent) { if (error_t error = writeAll(mX11ErrorPipe[STDOUT_FILENO], errorEvent, sizeof(XErrorEvent))) @@ -1016,8 +946,6 @@ void Core::runEventLoop(Window rootWindow) { XEvent event; bool keyReleaseExpected = false; - const QString superLeft = QString::fromUtf8(XKeysymToString(XK_Super_L)); - const QString superRight = QString::fromUtf8(XKeysymToString(XK_Super_R)); QSet allModifiers; unsigned int allShifts = ShiftMask | ControlMask | AltMask | MetaMask | Level3Mask | Level5Mask; unsigned int ignoreMask = 0xff ^ allShifts; @@ -1038,10 +966,12 @@ void Core::runEventLoop(Window rootWindow) if ((event.type == KeyRelease) && !keyReleaseExpected) { // pop event from the x11 queue and do nothing + log(LOG_DEBUG, "Ignored KeyRelease 1 -> %08x %08x", event.xkey.state, event.xkey.keycode); XNextEvent(mDisplay, &event); + log(LOG_DEBUG, "Ignored KeyRelease 2 -> %08x %08x", event.xkey.state, event.xkey.keycode); continue; } - keyReleaseExpected = false; // Close time window for accepting meta keys. + keyReleaseExpected = false; // initialize for next event if (((event.type == KeyPress) || (event.type == KeyRelease)) && mDataMutex.tryLock(0)) { @@ -1049,6 +979,7 @@ void Core::runEventLoop(Window rootWindow) // pop event from the x11 queue and process it XNextEvent(mDisplay, &event); + log(LOG_DEBUG, "Handling %s -> %08x %08x", event.type == KeyPress ? "KeyPress" : "KeyRelease", event.xkey.state, event.xkey.keycode); if (mGrabbingShortcut) { @@ -1233,365 +1164,364 @@ void Core::runEventLoop(Window rootWindow) } else { - if (event.type == KeyRelease) + updateShortcutState(event, keyReleaseExpected, allShifts); + } + + } + else + { + // check for pending pipe requests from other thread + handlePendingEvents(event, rootWindow, signal, allModifiers); + } + } + } +} + +/** check for pending pipe requests from other thread */ +void Core::handlePendingEvents(XEvent& event, Window rootWindow, char& signal, const QSet& allModifiers) +{ + if ((event.type != KeyPress) && (event.type != KeyRelease)) { + XNextEvent(mDisplay, &event); + } + + pollfd fds[1]; + fds[0].fd = mX11RequestPipe[STDIN_FILENO]; + fds[0].events = POLLIN | POLLERR | POLLHUP; + if (poll(fds, 1, 0) >= 0) + { + if (fds[0].revents & POLLIN) + { + size_t X11Operation; + if (readX11PipeRequest(&X11Operation, sizeof(X11Operation))) + { + // pipe error + mX11EventLoopActive = false; + return; + } +// log(LOG_DEBUG, "X11Operation: %d", X11Operation); + + switch (X11Operation) + { + case X11_OP_StringToKeycode: + { + bool x11Error = false; + KeyCode keyCode = 0; + size_t length; + if (readX11PipeRequest(&length, sizeof(length))) + { + // pipe error + mX11EventLoopActive = false; + break; + } + if (length) + { + QScopedArrayPointer str(new char[length + 1]); + str[length] = '\0'; + if (readX11PipeRequest(str.data(), length)) { - event.xkey.state &= ~allShifts; // Modifier keys must not use shift states. + // pipe error + mX11EventLoopActive = false; + break; } + KeySym keySym = XStringToKeysym(str.data()); + lockX11Error(); + keyCode = XKeysymToKeycode(mDisplay, keySym); + x11Error = checkX11Error(); + } - X11Shortcut shortcutKey = qMakePair(static_cast(event.xkey.keycode), event.xkey.state & allShifts); - ShortcutByX11::const_iterator shortcutIt = mShortcutByX11.constFind(shortcutKey); - if (shortcutIt == mShortcutByX11.constEnd()) + signal = x11Error ? 1 : 0; + if (writeX11PipeResponse(&signal, sizeof(signal))) + { + // pipe error + mX11EventLoopActive = false; + break; + } + + if (!x11Error) + if (writeX11PipeResponse(&keyCode, sizeof(keyCode))) { - continue; + // pipe error + mX11EventLoopActive = false; + break; } - const QString& shortcut = shortcutIt.value(); + } + break; - if (event.type == KeyPress) + case X11_OP_KeycodeToString: + { + KeyCode keyCode; + bool x11Error = false; + if (readX11PipeRequest(&keyCode, sizeof(keyCode))) + { + // pipe error + mX11EventLoopActive = false; + break; + } + int keysymsPerKeycode; + lockX11Error(); + KeySym *keySyms = XGetKeyboardMapping(mDisplay, keyCode, 1, &keysymsPerKeycode); + x11Error = checkX11Error(); + char *str = nullptr; + + if (!x11Error) + { + KeySym keySym = 0; + if ((keysymsPerKeycode >= 2) && keySyms[1] && (keySyms[0] >= XK_a) && (keySyms[0] <= XK_z)) { - if ((shortcut == superLeft) || (shortcut == superRight)) - { - keyReleaseExpected = true; - continue; - } - log(LOG_DEBUG, "KeyPress %08x %08x %s", event.xkey.state & allShifts, event.xkey.keycode, qPrintable(shortcut)); + keySym = keySyms[1]; } - else + else if (keysymsPerKeycode >= 1) { - log(LOG_DEBUG, "KeyRelease %08x %08x %s", event.xkey.state & allShifts, event.xkey.keycode, qPrintable(shortcut)); + keySym = keySyms[0]; } - IdsByShortcut::iterator idsByShortcut = mIdsByShortcut.find(shortcut); - if (idsByShortcut != mIdsByShortcut.end()) + if (keySym) { - Ids &ids = idsByShortcut.value(); - switch (mMultipleActionsBehaviour) - { - case MULTIPLE_ACTIONS_BEHAVIOUR_FIRST: - { - Ids::iterator lastIds = ids.end(); - for (Ids::iterator idi = ids.begin(); idi != lastIds; ++idi) - if (mShortcutAndActionById[*idi].second->call()) - { - break; - } - } - break; - - case MULTIPLE_ACTIONS_BEHAVIOUR_LAST: - { - Ids::iterator firstIds = ids.begin(); - for (Ids::iterator idi = ids.end(); idi != firstIds;) - { - --idi; - if (mShortcutAndActionById[*idi].second->call()) - { - break; - } - } - } - break; - - case MULTIPLE_ACTIONS_BEHAVIOUR_NONE: - if (ids.size() == 1) - { - mShortcutAndActionById[*(ids.begin())].second->call(); - } - break; - - case MULTIPLE_ACTIONS_BEHAVIOUR_ALL: - { - Ids::iterator lastIds = ids.end(); - for (Ids::iterator idi = ids.begin(); idi != lastIds; ++idi) - { - mShortcutAndActionById[*idi].second->call(); - } - } - break; - - case MULTIPLE_ACTIONS_BEHAVIOUR__COUNT: break; // just a counter - } + str = XKeysymToString(keySym); } } - } - else - // check for pending pipe requests from other thread - { - if ((event.type != KeyPress) && (event.type != KeyRelease)) { - XNextEvent(mDisplay, &event); + signal = x11Error ? 1 : 0; + if (writeX11PipeResponse(&signal, sizeof(signal))) + { + // pipe error + mX11EventLoopActive = false; + break; } - pollfd fds[1]; - fds[0].fd = mX11RequestPipe[STDIN_FILENO]; - fds[0].events = POLLIN | POLLERR | POLLHUP; - if (poll(fds, 1, 0) >= 0) + if (!x11Error) { - if (fds[0].revents & POLLIN) + size_t length = 0; + if (str) { - size_t X11Operation; - if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &X11Operation, sizeof(X11Operation))) + length = strlen(str); + } + if (writeX11PipeResponse(&length, sizeof(length))) + { + // pipe error + mX11EventLoopActive = false; + break; + } + if (length) + { + if (writeX11PipeResponse(str, length)) { - log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); - close(mX11ResponsePipe[STDIN_FILENO]); + // pipe error mX11EventLoopActive = false; break; } -// log(LOG_DEBUG, "X11Operation: %d", X11Operation); + } + } + } + break; - switch (X11Operation) - { - case X11_OP_StringToKeycode: - { - bool x11Error = false; - KeyCode keyCode = 0; - size_t length; - if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &length, sizeof(length))) - { - log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); - close(mX11ResponsePipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } - if (length) - { - QScopedArrayPointer str(new char[length + 1]); - str[length] = '\0'; - if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], str.data(), length)) - { - log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); - close(mX11ResponsePipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } - KeySym keySym = XStringToKeysym(str.data()); - lockX11Error(); - keyCode = XKeysymToKeycode(mDisplay, keySym); - x11Error = checkX11Error(); - } + case X11_OP_XGrabKey: + { + X11Shortcut X11shortcut; + bool x11Error = false; + if (readX11PipeRequest(&X11shortcut.first, sizeof(X11shortcut.first))) + { + // pipe error + mX11EventLoopActive = false; + break; + } + if (readX11PipeRequest(&X11shortcut.second, sizeof(X11shortcut.second))) + { + // pipe error + mX11EventLoopActive = false; + break; + } - signal = x11Error ? 1 : 0; - if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &signal, sizeof(signal))) - { - log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); - close(mX11RequestPipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } + for (auto modifier : qAsConst(allModifiers)) + { + lockX11Error(); + XGrabKey(mDisplay, X11shortcut.first, X11shortcut.second | modifier, rootWindow, False, GrabModeAsync, GrabModeAsync); + bool x11e = checkX11Error(); + if (x11e) + { + log(LOG_DEBUG, "XGrabKey: %02x + %02x", X11shortcut.first, X11shortcut.second | modifier); + } + x11Error |= x11e; + } - if (!x11Error) - if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &keyCode, sizeof(keyCode))) - { - log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); - close(mX11RequestPipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } - } - break; + signal = x11Error ? 1 : 0; + if (writeX11PipeResponse(&signal, sizeof(signal))) + { + // pipe error + mX11EventLoopActive = false; + break; + } + } + break; - case X11_OP_KeycodeToString: - { - KeyCode keyCode; - bool x11Error = false; - if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &keyCode, sizeof(keyCode))) - { - log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); - close(mX11ResponsePipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } - int keysymsPerKeycode; - lockX11Error(); - KeySym *keySyms = XGetKeyboardMapping(mDisplay, keyCode, 1, &keysymsPerKeycode); - x11Error = checkX11Error(); - char *str = nullptr; + case X11_OP_XUngrabKey: + { + X11Shortcut X11shortcut; + bool x11Error = false; + if (readX11PipeRequest(&X11shortcut.first, sizeof(X11shortcut.first))) + { + // pipe error + mX11EventLoopActive = false; + break; + } + if (readX11PipeRequest(&X11shortcut.second, sizeof(X11shortcut.second))) + { + // pipe error + mX11EventLoopActive = false; + break; + } - if (!x11Error) - { - KeySym keySym = 0; - if ((keysymsPerKeycode >= 2) && keySyms[1] && (keySyms[0] >= XK_a) && (keySyms[0] <= XK_z)) - { - keySym = keySyms[1]; - } - else if (keysymsPerKeycode >= 1) - { - keySym = keySyms[0]; - } + lockX11Error(); + for (auto modifier : qAsConst(allModifiers)) + { + XUngrabKey(mDisplay, X11shortcut.first, X11shortcut.second | modifier, rootWindow); + } + x11Error = checkX11Error(); - if (keySym) - { - str = XKeysymToString(keySym); - } - } + signal = x11Error ? 1 : 0; + if (writeX11PipeResponse(&signal, sizeof(signal))) + { + // pipe error + mX11EventLoopActive = false; + break; + } + } + break; - signal = x11Error ? 1 : 0; - if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &signal, sizeof(signal))) - { - log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); - close(mX11RequestPipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } + case X11_OP_XGrabKeyboard: + { + lockX11Error(); + int result = XGrabKeyboard(mDisplay, rootWindow, False, GrabModeAsync, GrabModeAsync, CurrentTime); + bool x11Error = checkX11Error(); + if (!result && x11Error) + { + result = -1; + } - if (!x11Error) - { - size_t length = 0; - if (str) - { - length = strlen(str); - } - if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &length, sizeof(length))) - { - log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); - close(mX11RequestPipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } - if (length) - { - if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], str, length)) - { - log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); - close(mX11RequestPipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } - } - } - } - break; + if (writeX11PipeResponse(&result, sizeof(result))) { + // pipe error + mX11EventLoopActive = false; + break; + } - case X11_OP_XGrabKey: - { - X11Shortcut X11shortcut; - bool x11Error = false; - if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &X11shortcut.first, sizeof(X11shortcut.first))) - { - log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); - close(mX11ResponsePipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } - if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &X11shortcut.second, sizeof(X11shortcut.second))) - { - log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); - close(mX11ResponsePipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } + mDataMutex.lock(); + mGrabbingShortcut = true; + mDataMutex.unlock(); + } + break; - QSet::const_iterator lastAllModifiers = allModifiers.cend(); - for (QSet::const_iterator modifiers = allModifiers.cbegin(); modifiers != lastAllModifiers; ++modifiers) - { - lockX11Error(); - XGrabKey(mDisplay, X11shortcut.first, X11shortcut.second | *modifiers, rootWindow, False, GrabModeAsync, GrabModeAsync); - bool x11e = checkX11Error(); - if (x11e) - { - log(LOG_DEBUG, "XGrabKey: %02x + %02x", X11shortcut.first, X11shortcut.second | *modifiers); - } - x11Error |= x11e; - } + case X11_OP_XUngrabKeyboard: + { + lockX11Error(); + XUngrabKeyboard(mDisplay, CurrentTime); + bool x11Error = checkX11Error(); + + signal = x11Error ? 1 : 0; + if (writeX11PipeResponse(&signal, sizeof(signal))) { + // pipe error + mX11EventLoopActive = false; + break; + } - signal = x11Error ? 1 : 0; - if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &signal, sizeof(signal))) - { - log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); - close(mX11RequestPipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } - } - break; + mDataMutex.lock(); + mGrabbingShortcut = false; + mDataMutex.unlock(); + } + break; + } // end of switch-case - case X11_OP_XUngrabKey: - { - X11Shortcut X11shortcut; - bool x11Error = false; - if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &X11shortcut.first, sizeof(X11shortcut.first))) - { - log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); - close(mX11ResponsePipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } - if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &X11shortcut.second, sizeof(X11shortcut.second))) - { - log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); - close(mX11ResponsePipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } + } + } +} - lockX11Error(); - QSet::const_iterator lastAllModifiers = allModifiers.cend(); - for (QSet::const_iterator modifiers = allModifiers.cbegin(); modifiers != lastAllModifiers; ++modifiers) - { - XUngrabKey(mDisplay, X11shortcut.first, X11shortcut.second | *modifiers, rootWindow); - } - x11Error = checkX11Error(); +void Core::updateShortcutState(XEvent& event, bool& keyReleaseExpected, unsigned int allShifts) +{ + if (event.type == KeyRelease) + { + log(LOG_DEBUG, "KeyRelease 1 -> %08x %08x", event.xkey.state, event.xkey.keycode); + event.xkey.state &= ~allShifts; // Modifier keys must not use shift states. + log(LOG_DEBUG, "KeyRelease 2 -> %08x %08x", event.xkey.state, event.xkey.keycode); + } - signal = x11Error ? 1 : 0; - if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &signal, sizeof(signal))) - { - log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); - close(mX11RequestPipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } - } - break; + X11Shortcut shortcutKey = qMakePair(static_cast(event.xkey.keycode), event.xkey.state & allShifts); + ShortcutByX11::const_iterator shortcutIt = mShortcutByX11.constFind(shortcutKey); + if (shortcutIt == mShortcutByX11.constEnd()) + { + return; + } - case X11_OP_XGrabKeyboard: - { - lockX11Error(); - int result = XGrabKeyboard(mDisplay, rootWindow, False, GrabModeAsync, GrabModeAsync, CurrentTime); - bool x11Error = checkX11Error(); - if (!result && x11Error) - { - result = -1; - } + const QString& shortcut = shortcutIt.value(); + if (event.type == KeyPress) + { + log(LOG_DEBUG, "KeyPress %08x %08x %s ", event.xkey.state, event.xkey.keycode, qPrintable(shortcut)); + if ((shortcut == superLeft) || (shortcut == superRight)) + { + keyReleaseExpected = true; + return; + } + } + else + { + log(LOG_DEBUG, "KeyRelease 3 -> %08x %08x %s", event.xkey.state, event.xkey.keycode, qPrintable(shortcut)); + } - if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &result, sizeof(result))) - { - log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); - close(mX11RequestPipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } - mDataMutex.lock(); - mGrabbingShortcut = true; - mDataMutex.unlock(); - } - break; + IdsByShortcut::iterator idsByShortcut = mIdsByShortcut.find(shortcut); + if (idsByShortcut != mIdsByShortcut.end()) + { + this->shortcut(idsByShortcut.value()); + } +} - case X11_OP_XUngrabKeyboard: - { - lockX11Error(); - XUngrabKeyboard(mDisplay, CurrentTime); - bool x11Error = checkX11Error(); +void Core::shortcut(const Ids& ids) const +{ + switch (mMultipleActionsBehaviour) + { + case MULTIPLE_ACTIONS_BEHAVIOUR_FIRST: + { + auto lastIds = ids.end(); + for (auto idi = ids.begin(); idi != lastIds; ++idi) + { + if (mShortcutAndActionById[*idi].second->call()) + { + break; + } + } + } + break; - signal = x11Error ? 1 : 0; - if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &signal, sizeof(signal))) - { - log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); - close(mX11RequestPipe[STDIN_FILENO]); - mX11EventLoopActive = false; - break; - } + case MULTIPLE_ACTIONS_BEHAVIOUR_LAST: + { + auto firstIds = ids.begin(); + for (auto idi = ids.end(); idi != firstIds;) + { + --idi; + if (mShortcutAndActionById[*idi].second->call()) + { + break; + } + } + } + break; - mDataMutex.lock(); - mGrabbingShortcut = false; - mDataMutex.unlock(); - } - break; - } // end of switch-case + case MULTIPLE_ACTIONS_BEHAVIOUR_NONE: + if (ids.size() == 1) + { + mShortcutAndActionById[*(ids.begin())].second->call(); + } + break; - } - } - } + case MULTIPLE_ACTIONS_BEHAVIOUR_ALL: + { + auto lastIds = ids.end(); + for (auto idi = ids.begin(); idi != lastIds; ++idi) + { + mShortcutAndActionById[*idi].second->call(); } } + break; + + case MULTIPLE_ACTIONS_BEHAVIOUR__COUNT: break; // just a counter + } } void Core::serviceDisappeared(const QString &sender) @@ -2006,7 +1936,6 @@ QString Core::checkShortcut(const QString &shortcut, X11Shortcut &X11shortcut) QPair Core::addOrRegisterClientAction(const QString &shortcut, const QDBusObjectPath &path, const QString &description, const QString &sender) { X11Shortcut X11shortcut; - QString newShortcut = checkShortcut(shortcut, X11shortcut); // if (newShortcut.isEmpty()) // { @@ -2029,13 +1958,13 @@ QPair Core::addOrRegisterClientAction(const QString &shortc mIdsByShortcut[newShortcut].insert(id); } - dynamic_cast(shortcutAndAction.second)->appeared(QDBusConnection::sessionBus(), sender); + auto action = static_cast(shortcutAndAction.second); + action->appeared(QDBusConnection::sessionBus(), sender); return qMakePair(newShortcut, id); } qulonglong id = ++mLastId; - if (!sender.isEmpty() && !newShortcut.isEmpty()) { newShortcut = grabOrReuseKey(X11shortcut, newShortcut); @@ -2043,11 +1972,10 @@ QPair Core::addOrRegisterClientAction(const QString &shortc } mIdByClientPath[path] = id; - ClientAction *clientAction = sender.isEmpty() ? new ClientAction(this, path, description) : new ClientAction(this, QDBusConnection::sessionBus(), sender, path, description); + auto clientAction = sender.isEmpty() ? new ClientAction(mCoreLogger.get(), path, description) : new ClientAction(mCoreLogger.get(), QDBusConnection::sessionBus(), sender, path, description); mShortcutAndActionById[id] = qMakePair(newShortcut, clientAction); log(LOG_INFO, "addClientAction shortcut:'%s' id:%llu", qPrintable(newShortcut), id); - return qMakePair(newShortcut, id); } @@ -2100,7 +2028,8 @@ qulonglong Core::registerClientAction(const QString &shortcut, const QDBusObject QMutexLocker lock(&mDataMutex); - return addOrRegisterClientAction(shortcut, path, description, QString()).second; + auto action = addOrRegisterClientAction(shortcut, path, description, QString()); + return action.second; } void Core::addMethodAction(QPair &result, const QString &shortcut, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description) @@ -2128,7 +2057,7 @@ void Core::addMethodAction(QPair &result, const QString &sh qulonglong id = ++mLastId; mIdsByShortcut[newShortcut].insert(id); - mShortcutAndActionById[id] = qMakePair(newShortcut, new MethodAction(this, QDBusConnection::sessionBus(), service, path, interface, method, description)); + mShortcutAndActionById[id] = qMakePair(newShortcut, new MethodAction(mCoreLogger.get(), QDBusConnection::sessionBus(), service, path, interface, method, description)); log(LOG_INFO, "addMethodAction shortcut:'%s' id:%llu", qPrintable(newShortcut), id); @@ -2169,7 +2098,7 @@ void Core::addCommandAction(QPair &result, const QString &s qulonglong id = ++mLastId; mIdsByShortcut[newShortcut].insert(id); - mShortcutAndActionById[id] = qMakePair(newShortcut, new CommandAction(this, command, arguments, description)); + mShortcutAndActionById[id] = qMakePair(newShortcut, new CommandAction(mCoreLogger.get(), command, arguments, description)); log(LOG_INFO, "addCommandAction shortcut:'%s' id:%llu", qPrintable(newShortcut), id); @@ -2280,7 +2209,7 @@ void Core::modifyMethodAction(bool &result, const qulonglong &id, const QString bool isEnabled = action->isEnabled(); delete action; - MethodAction *newAction = new MethodAction(this, QDBusConnection::sessionBus(), service, path, interface, method, description); + auto newAction = new MethodAction(mCoreLogger.get(), QDBusConnection::sessionBus(), service, path, interface, method, description); newAction->setEnabled(isEnabled); shortcutAndActionById.value().second = newAction; @@ -2314,7 +2243,7 @@ void Core::modifyCommandAction(bool &result, const qulonglong &id, const QString bool isEnabled = action->isEnabled(); delete action; - CommandAction *newAction = new CommandAction(this, command, arguments, description); + auto newAction = new CommandAction(mCoreLogger.get(), command, arguments, description); newAction->setEnabled(isEnabled); shortcutAndActionById.value().second = newAction; diff --git a/daemon/core.h b/daemon/core.h index 9bf9da7..de0761b 100644 --- a/daemon/core.h +++ b/daemon/core.h @@ -40,6 +40,7 @@ #include "meta_types.h" #include "log_target.h" +#include "pipe_utils.h" extern "C" { #include @@ -49,6 +50,7 @@ extern "C" { #undef Bool } +#include class QTimer; class DaemonAdaptor; @@ -68,16 +70,21 @@ class QOrderedSet : public QMap } }; -class Core : public QThread, public LogTarget +class Core : public QThread { Q_OBJECT + public: Core(bool useSyslog, bool minLogLevelSet, int minLogLevel, const QStringList &configFiles, bool multipleActionsBehaviourSet, MultipleActionsBehaviour multipleActionsBehaviour, QObject *parent = nullptr); ~Core() override; bool ready() const { return mReady; } - void log(int level, const char *format, ...) const override; + template + void log(int level, const char *format, Args&&... args) const { + // NOTE: If the logger is unassigned the SEGFAULT is intentional! + mCoreLogger->log(level, format, std::forward(args)...); + } signals: void onShortcutGrabbed(); @@ -146,6 +153,7 @@ class Core : public QThread, public LogTarget void grabShortcut(const uint &timeout, QString &shortcut, bool &failed, bool &cancelled, bool &timedout, const QDBusMessage &message); void cancelShortcutGrab(); + void shortcut(const Ids& ids) const; void shortcutGrabbed(); void shortcutGrabTimedout(); @@ -171,12 +179,34 @@ class Core : public QThread, public LogTarget void run() override; void runEventLoop(Window rootWindow); + void handlePendingEvents(XEvent& event, Window rootWindow, char& signal, const QSet& allModifiers); + void updateShortcutState(XEvent& event, bool& keyReleaseExpected, unsigned int allShifts); KeyCode remoteStringToKeycode(const QString &str); QString remoteKeycodeToString(KeyCode keyCode); bool remoteXGrabKey(const X11Shortcut &X11shortcut); bool remoteXUngrabKey(const X11Shortcut &X11shortcut); + template + error_t readX11PipeRequest(T* ptr, size_t len) { + error_t error = readAll(mX11RequestPipe[STDIN_FILENO], ptr, len); + if (error) { + log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); + close(mX11ResponsePipe[STDIN_FILENO]); + } + return error; + } + + template + error_t writeX11PipeResponse(T* ptr, size_t len) { + error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], ptr, len); + if (error) { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + } + return error; + } + QString grabOrReuseKey(const X11Shortcut &X11shortcut, const QString &shortcut); QString checkShortcut(const QString &shortcut, X11Shortcut &X11shortcut); @@ -396,10 +426,9 @@ class Core : public QThread, public LogTarget bool waitForX11Error(int level, uint timeout); private: - bool mReady; - bool mUseSyslog; - int mMinLogLevel; + std::unique_ptr mCoreLogger; + bool mReady; int mX11ErrorPipe[2]; int mX11RequestPipe[2]; int mX11ResponsePipe[2]; @@ -434,6 +463,9 @@ class Core : public QThread, public LogTarget const unsigned int Level3Mask = Mod5Mask; // note: mask swapped const unsigned int Level5Mask = Mod3Mask; // note: mask swapped + const QString superLeft = QString::fromUtf8(XKeysymToString(XK_Super_L)); + const QString superRight = QString::fromUtf8(XKeysymToString(XK_Super_R)); + MultipleActionsBehaviour mMultipleActionsBehaviour; bool mAllowGrabLocks; diff --git a/daemon/log_target.cpp b/daemon/log_target.cpp index 656b110..71051a6 100644 --- a/daemon/log_target.cpp +++ b/daemon/log_target.cpp @@ -26,12 +26,32 @@ * END_COMMON_COPYRIGHT_HEADER */ #include "log_target.h" +#include - -LogTarget::LogTarget() +LogTarget::LogTarget(int minLogLevel, bool useSyslog) { + mMinLogLevel = minLogLevel; + mUseSyslog = useSyslog; } -LogTarget::~LogTarget() +void LogTarget::log(int level, const char *format, ...) { + if (level > mMinLogLevel) + { + return; + } + + va_list ap; + va_start(ap, format); + if (mUseSyslog) + { + vsyslog(LOG_MAKEPRI(LOG_USER, level), format, ap); + } + else + { + fprintf(stderr, "[%s] ", strLevel(level)); + vfprintf(stderr, format, ap); + fprintf(stderr, "\n"); + } + va_end(ap); } diff --git a/daemon/log_target.h b/daemon/log_target.h index 6ca4590..f292d30 100644 --- a/daemon/log_target.h +++ b/daemon/log_target.h @@ -25,20 +25,60 @@ * * END_COMMON_COPYRIGHT_HEADER */ -#ifndef GLOBAL_ACTION_DAEMON__LOG_TARGET__INCLUDED -#define GLOBAL_ACTION_DAEMON__LOG_TARGET__INCLUDED - +#pragma once #include +#include +class LogTarget { +public: + int mMinLogLevel = LOG_INFO; + bool mUseSyslog = false; -class LogTarget -{ public: - LogTarget(); - virtual ~LogTarget(); + LogTarget(int minLogLevel = LOG_INFO, bool useSyslog = false); - virtual void log(int level, const char *format, ...) const = 0; -}; + void log(int level, const char *format, ...); + + static constexpr auto strLevel(int level) + { + switch (level) { + case LOG_EMERG: return "Emergency"; + case LOG_ALERT: return "Alert"; + case LOG_CRIT: return "Critical"; + case LOG_ERR: return "Error"; + case LOG_WARNING: return "Warning"; + case LOG_NOTICE: return "Notice"; + case LOG_INFO: return "Info"; + case LOG_DEBUG: return "Debug"; + } -#endif // GLOBAL_ACTION_DAEMON__LOG_TARGET__INCLUDED + return ""; // fallthrough + } + + static constexpr auto levelFromStr(const char* str) { + if (str == nullptr) { + // error: null string + return -2; + } + + if (std::strcmp(str, "error") == 0) { + return LOG_ERR; + } + if (std::strcmp(str, "warning") == 0) { + return LOG_WARNING; + } + if (std::strcmp(str, "notice") == 0) { + return LOG_NOTICE; + } + if (std::strcmp(str, "info") == 0) { + return LOG_INFO; + } + if (std::strcmp(str, "debug") == 0) { + return LOG_DEBUG; + } + + // error: unknown log level string + return -1; + } +};