* speeds up incremental builds as changes to a header will not always need the full mocs_compilation.cpp for all the target's headers rebuild, while having a moc file sourced into a source file only adds minor extra costs, due to small own code and the used headers usually already covered by the source file, being for the same class/struct * seems to not slow down clean builds, due to empty mocs_compilation.cpp resulting in those quickly processed, while the minor extra cost of the sourced moc files does not outweigh that in summary. Measured times actually improved by some percent points. (ideally CMake would just skip empty mocs_compilation.cpp & its object file one day) * enables compiler to see all methods of a class in same compilation unit to do some sanity checks * potentially more inlining in general, due to more in the compilation unit * allows to keep using more forward declarations in the header, as with the moc code being sourced into the cpp file there definitions can be ensured and often are already for the needs of the normal class methods
306 lines
11 KiB
C++
306 lines
11 KiB
C++
/*
|
|
KWin - the KDE window manager
|
|
This file is part of the KDE project.
|
|
|
|
SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
// own
|
|
#include "globalshortcuts.h"
|
|
// config
|
|
#include <config-kwin.h>
|
|
// kwin
|
|
#include "gestures.h"
|
|
#include "libkwineffects/kwinglobals.h"
|
|
#include "main.h"
|
|
#include "utils/common.h"
|
|
// KDE
|
|
#include <kglobalaccel_interface.h>
|
|
#include <kglobalacceld.h>
|
|
// Qt
|
|
#include <QAction>
|
|
// system
|
|
#include <signal.h>
|
|
#include <variant>
|
|
|
|
namespace KWin
|
|
{
|
|
GlobalShortcut::GlobalShortcut(Shortcut &&sc, QAction *action)
|
|
: m_shortcut(sc)
|
|
, m_action(action)
|
|
{
|
|
if (auto swipeGesture = std::get_if<RealtimeFeedbackSwipeShortcut>(&sc)) {
|
|
m_swipeGesture = std::make_unique<SwipeGesture>();
|
|
m_swipeGesture->setDirection(swipeGesture->direction);
|
|
m_swipeGesture->setMinimumDelta(QPointF(200, 200));
|
|
m_swipeGesture->setMaximumFingerCount(swipeGesture->fingerCount);
|
|
m_swipeGesture->setMinimumFingerCount(swipeGesture->fingerCount);
|
|
QObject::connect(m_swipeGesture.get(), &SwipeGesture::triggered, m_action, &QAction::trigger, Qt::QueuedConnection);
|
|
QObject::connect(m_swipeGesture.get(), &SwipeGesture::cancelled, m_action, &QAction::trigger, Qt::QueuedConnection);
|
|
if (swipeGesture->progressCallback) {
|
|
QObject::connect(m_swipeGesture.get(), &SwipeGesture::progress, swipeGesture->progressCallback);
|
|
}
|
|
} else if (auto pinchGesture = std::get_if<RealtimeFeedbackPinchShortcut>(&sc)) {
|
|
m_pinchGesture = std::make_unique<PinchGesture>();
|
|
m_pinchGesture->setDirection(pinchGesture->direction);
|
|
m_pinchGesture->setMaximumFingerCount(pinchGesture->fingerCount);
|
|
m_pinchGesture->setMinimumFingerCount(pinchGesture->fingerCount);
|
|
QObject::connect(m_pinchGesture.get(), &PinchGesture::triggered, m_action, &QAction::trigger, Qt::QueuedConnection);
|
|
QObject::connect(m_pinchGesture.get(), &PinchGesture::cancelled, m_action, &QAction::trigger, Qt::QueuedConnection);
|
|
if (pinchGesture->scaleCallback) {
|
|
QObject::connect(m_pinchGesture.get(), &PinchGesture::progress, pinchGesture->scaleCallback);
|
|
}
|
|
}
|
|
}
|
|
|
|
GlobalShortcut::~GlobalShortcut()
|
|
{
|
|
}
|
|
|
|
QAction *GlobalShortcut::action() const
|
|
{
|
|
return m_action;
|
|
}
|
|
|
|
void GlobalShortcut::invoke() const
|
|
{
|
|
QMetaObject::invokeMethod(m_action, &QAction::trigger, Qt::QueuedConnection);
|
|
}
|
|
|
|
const Shortcut &GlobalShortcut::shortcut() const
|
|
{
|
|
return m_shortcut;
|
|
}
|
|
|
|
SwipeGesture *GlobalShortcut::swipeGesture() const
|
|
{
|
|
return m_swipeGesture.get();
|
|
}
|
|
|
|
PinchGesture *GlobalShortcut::pinchGesture() const
|
|
{
|
|
return m_pinchGesture.get();
|
|
}
|
|
|
|
GlobalShortcutsManager::GlobalShortcutsManager(QObject *parent)
|
|
: QObject(parent)
|
|
, m_touchpadGestureRecognizer(new GestureRecognizer(this))
|
|
, m_touchscreenGestureRecognizer(new GestureRecognizer(this))
|
|
{
|
|
}
|
|
|
|
GlobalShortcutsManager::~GlobalShortcutsManager()
|
|
{
|
|
}
|
|
|
|
void GlobalShortcutsManager::init()
|
|
{
|
|
if (kwinApp()->shouldUseWaylandForCompositing()) {
|
|
qputenv("KGLOBALACCELD_PLATFORM", QByteArrayLiteral("org.kde.kwin"));
|
|
m_kglobalAccel = std::make_unique<KGlobalAccelD>();
|
|
if (!m_kglobalAccel->init()) {
|
|
qCDebug(KWIN_CORE) << "Init of kglobalaccel failed";
|
|
m_kglobalAccel.reset();
|
|
} else {
|
|
qCDebug(KWIN_CORE) << "KGlobalAcceld inited";
|
|
}
|
|
}
|
|
}
|
|
|
|
void GlobalShortcutsManager::objectDeleted(QObject *object)
|
|
{
|
|
auto it = m_shortcuts.begin();
|
|
while (it != m_shortcuts.end()) {
|
|
if (it->action() == object) {
|
|
it = m_shortcuts.erase(it);
|
|
} else {
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool GlobalShortcutsManager::add(GlobalShortcut sc, DeviceType device)
|
|
{
|
|
const auto &recognizer = device == DeviceType::Touchpad ? m_touchpadGestureRecognizer : m_touchscreenGestureRecognizer;
|
|
if (std::holds_alternative<RealtimeFeedbackSwipeShortcut>(sc.shortcut())) {
|
|
recognizer->registerSwipeGesture(sc.swipeGesture());
|
|
} else if (std::holds_alternative<RealtimeFeedbackPinchShortcut>(sc.shortcut())) {
|
|
recognizer->registerPinchGesture(sc.pinchGesture());
|
|
}
|
|
connect(sc.action(), &QAction::destroyed, this, &GlobalShortcutsManager::objectDeleted);
|
|
m_shortcuts.push_back(std::move(sc));
|
|
return true;
|
|
}
|
|
|
|
void GlobalShortcutsManager::registerPointerShortcut(QAction *action, Qt::KeyboardModifiers modifiers, Qt::MouseButtons pointerButtons)
|
|
{
|
|
add(GlobalShortcut(PointerButtonShortcut{modifiers, pointerButtons}, action));
|
|
}
|
|
|
|
void GlobalShortcutsManager::registerAxisShortcut(QAction *action, Qt::KeyboardModifiers modifiers, PointerAxisDirection axis)
|
|
{
|
|
add(GlobalShortcut(PointerAxisShortcut{modifiers, axis}, action));
|
|
}
|
|
|
|
void GlobalShortcutsManager::registerTouchpadSwipe(SwipeDirection direction, uint32_t fingerCount, QAction *action, std::function<void(qreal)> progressCallback)
|
|
{
|
|
add(GlobalShortcut(RealtimeFeedbackSwipeShortcut{DeviceType::Touchpad, direction, progressCallback, fingerCount}, action), DeviceType::Touchpad);
|
|
}
|
|
|
|
void GlobalShortcutsManager::registerTouchpadPinch(PinchDirection direction, uint32_t fingerCount, QAction *action, std::function<void(qreal)> progressCallback)
|
|
{
|
|
add(GlobalShortcut(RealtimeFeedbackPinchShortcut{direction, progressCallback, fingerCount}, action), DeviceType::Touchpad);
|
|
}
|
|
|
|
void GlobalShortcutsManager::registerTouchscreenSwipe(SwipeDirection direction, uint32_t fingerCount, QAction *action, std::function<void(qreal)> progressCallback)
|
|
{
|
|
add(GlobalShortcut(RealtimeFeedbackSwipeShortcut{DeviceType::Touchscreen, direction, progressCallback, fingerCount}, action), DeviceType::Touchscreen);
|
|
}
|
|
|
|
void GlobalShortcutsManager::forceRegisterTouchscreenSwipe(SwipeDirection direction, uint32_t fingerCount, QAction *action, std::function<void(qreal)> progressCallback)
|
|
{
|
|
GlobalShortcut shortcut{RealtimeFeedbackSwipeShortcut{DeviceType::Touchscreen, direction, progressCallback, fingerCount}, action};
|
|
const auto it = std::find_if(m_shortcuts.begin(), m_shortcuts.end(), [&shortcut](const auto &s) {
|
|
return shortcut.shortcut() == s.shortcut();
|
|
});
|
|
if (it != m_shortcuts.end()) {
|
|
m_shortcuts.erase(it);
|
|
}
|
|
m_touchscreenGestureRecognizer->registerSwipeGesture(shortcut.swipeGesture());
|
|
connect(shortcut.action(), &QAction::destroyed, this, &GlobalShortcutsManager::objectDeleted);
|
|
m_shortcuts.push_back(std::move(shortcut));
|
|
}
|
|
|
|
bool GlobalShortcutsManager::processKey(Qt::KeyboardModifiers mods, int keyQt)
|
|
{
|
|
if (m_kglobalAccelInterface) {
|
|
if (!keyQt && !mods) {
|
|
return false;
|
|
}
|
|
auto check = [this](Qt::KeyboardModifiers mods, int keyQt) {
|
|
bool retVal = false;
|
|
QMetaObject::invokeMethod(m_kglobalAccelInterface,
|
|
"checkKeyPressed",
|
|
Qt::DirectConnection,
|
|
Q_RETURN_ARG(bool, retVal),
|
|
Q_ARG(int, int(mods) | keyQt));
|
|
return retVal;
|
|
};
|
|
if (check(mods, keyQt)) {
|
|
return true;
|
|
} else if (keyQt == Qt::Key_Backtab) {
|
|
// KGlobalAccel on X11 has some workaround for Backtab
|
|
// see kglobalaccel/src/runtime/plugins/xcb/kglobalccel_x11.cpp method x11KeyPress
|
|
// Apparently KKeySequenceWidget captures Shift+Tab instead of Backtab
|
|
// thus if the key is backtab we should adjust to add shift again and use tab
|
|
// in addition KWin registers the shortcut incorrectly as Alt+Shift+Backtab
|
|
// this should be changed to either Alt+Backtab or Alt+Shift+Tab to match KKeySequenceWidget
|
|
// trying the variants
|
|
if (check(mods | Qt::ShiftModifier, keyQt)) {
|
|
return true;
|
|
}
|
|
if (check(mods | Qt::ShiftModifier, Qt::Key_Tab)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool GlobalShortcutsManager::processKeyRelease(Qt::KeyboardModifiers mods, int keyQt)
|
|
{
|
|
if (m_kglobalAccelInterface) {
|
|
QMetaObject::invokeMethod(m_kglobalAccelInterface,
|
|
"checkKeyReleased",
|
|
Qt::DirectConnection,
|
|
Q_ARG(int, int(mods) | keyQt));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template<typename ShortcutKind, typename... Args>
|
|
bool match(QVector<GlobalShortcut> &shortcuts, Args... args)
|
|
{
|
|
for (auto &sc : shortcuts) {
|
|
if (std::holds_alternative<ShortcutKind>(sc.shortcut())) {
|
|
if (std::get<ShortcutKind>(sc.shortcut()) == ShortcutKind{args...}) {
|
|
sc.invoke();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// TODO(C++20): use ranges for a nicer way of filtering by shortcut type
|
|
bool GlobalShortcutsManager::processPointerPressed(Qt::KeyboardModifiers mods, Qt::MouseButtons pointerButtons)
|
|
{
|
|
return match<PointerButtonShortcut>(m_shortcuts, mods, pointerButtons);
|
|
}
|
|
|
|
bool GlobalShortcutsManager::processAxis(Qt::KeyboardModifiers mods, PointerAxisDirection axis)
|
|
{
|
|
return match<PointerAxisShortcut>(m_shortcuts, mods, axis);
|
|
}
|
|
|
|
void GlobalShortcutsManager::processSwipeStart(DeviceType device, uint fingerCount)
|
|
{
|
|
if (device == DeviceType::Touchpad) {
|
|
m_touchpadGestureRecognizer->startSwipeGesture(fingerCount);
|
|
} else {
|
|
m_touchscreenGestureRecognizer->startSwipeGesture(fingerCount);
|
|
}
|
|
}
|
|
|
|
void GlobalShortcutsManager::processSwipeUpdate(DeviceType device, const QPointF &delta)
|
|
{
|
|
if (device == DeviceType::Touchpad) {
|
|
m_touchpadGestureRecognizer->updateSwipeGesture(delta);
|
|
} else {
|
|
m_touchscreenGestureRecognizer->updateSwipeGesture(delta);
|
|
}
|
|
}
|
|
|
|
void GlobalShortcutsManager::processSwipeCancel(DeviceType device)
|
|
{
|
|
if (device == DeviceType::Touchpad) {
|
|
m_touchpadGestureRecognizer->cancelSwipeGesture();
|
|
} else {
|
|
m_touchscreenGestureRecognizer->cancelSwipeGesture();
|
|
}
|
|
}
|
|
|
|
void GlobalShortcutsManager::processSwipeEnd(DeviceType device)
|
|
{
|
|
if (device == DeviceType::Touchpad) {
|
|
m_touchpadGestureRecognizer->endSwipeGesture();
|
|
} else {
|
|
m_touchscreenGestureRecognizer->endSwipeGesture();
|
|
}
|
|
// TODO: cancel on Wayland Seat if one triggered
|
|
}
|
|
|
|
void GlobalShortcutsManager::processPinchStart(uint fingerCount)
|
|
{
|
|
m_touchpadGestureRecognizer->startPinchGesture(fingerCount);
|
|
}
|
|
|
|
void GlobalShortcutsManager::processPinchUpdate(qreal scale, qreal angleDelta, const QPointF &delta)
|
|
{
|
|
m_touchpadGestureRecognizer->updatePinchGesture(scale, angleDelta, delta);
|
|
}
|
|
|
|
void GlobalShortcutsManager::processPinchCancel()
|
|
{
|
|
m_touchpadGestureRecognizer->cancelPinchGesture();
|
|
}
|
|
|
|
void GlobalShortcutsManager::processPinchEnd()
|
|
{
|
|
m_touchpadGestureRecognizer->endPinchGesture();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
#include "moc_globalshortcuts.cpp"
|