diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c6b680344..c042ff78f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -469,6 +469,7 @@ if(KWIN_BUILD_TABBOX) tabbox/tabboxconfig.cpp tabbox/tabboxhandler.cpp tabbox/tabbox_logging.cpp + tabbox/x11_filter.cpp ) endif() diff --git a/events.cpp b/events.cpp index 933dc38b31..1770719c9f 100644 --- a/events.cpp +++ b/events.cpp @@ -266,11 +266,6 @@ bool Workspace::workspaceEvent(xcb_generic_event_t *e) case XCB_BUTTON_PRESS: case XCB_BUTTON_RELEASE: { auto *mouseEvent = reinterpret_cast(e); -#ifdef KWIN_BUILD_TABBOX - if (TabBox::TabBox::self()->isGrabbed()) { - return TabBox::TabBox::self()->handleMouseEvent(mouseEvent); - } -#endif if (effects && static_cast(effects)->checkInputWindowEvent(mouseEvent)) { return true; } @@ -283,12 +278,6 @@ bool Workspace::workspaceEvent(xcb_generic_event_t *e) } auto *mouseEvent = reinterpret_cast(e); const QPoint rootPos(mouseEvent->root_x, mouseEvent->root_y); -#ifdef KWIN_BUILD_TABBOX - if (TabBox::TabBox::self()->isGrabbed()) { - ScreenEdges::self()->check(rootPos, QDateTime::fromMSecsSinceEpoch(xTime()), true); - return TabBox::TabBox::self()->handleMouseEvent(mouseEvent); - } -#endif if (effects && static_cast(effects)->checkInputWindowEvent(mouseEvent)) { return true; } @@ -299,27 +288,6 @@ bool Workspace::workspaceEvent(xcb_generic_event_t *e) } break; } - case XCB_KEY_PRESS: { - int keyQt; - xcb_key_press_event_t *event = reinterpret_cast(e); - KKeyServer::xcbKeyPressEventToQt(event, &keyQt); -// qDebug() << "Workspace::keyPress( " << keyQt << " )"; -#ifdef KWIN_BUILD_TABBOX - if (TabBox::TabBox::self()->isGrabbed()) { - TabBox::TabBox::self()->keyPress(keyQt); - return true; - } -#endif - break; - } - case XCB_KEY_RELEASE: -#ifdef KWIN_BUILD_TABBOX - if (TabBox::TabBox::self()->isGrabbed()) { - TabBox::TabBox::self()->keyRelease(reinterpret_cast(e)); - return true; - } -#endif - break; case XCB_CONFIGURE_NOTIFY: if (reinterpret_cast(e)->event == rootWindow()) markXStackingOrderAsDirty(); diff --git a/tabbox/tabbox.cpp b/tabbox/tabbox.cpp index 1438dd6743..ce825bd3fd 100644 --- a/tabbox/tabbox.cpp +++ b/tabbox/tabbox.cpp @@ -29,6 +29,7 @@ along with this program. If not, see . #include "tabbox/tabboxconfig.h" #include "tabbox/desktopchain.h" #include "tabbox/tabbox_logging.h" +#include "tabbox/x11_filter.h" // kwin #ifdef KWIN_BUILD_ACTIVITIES #include "activities.h" @@ -504,6 +505,10 @@ TabBox::TabBox(QObject *parent) m_tabBoxMode = TabBoxDesktopMode; // init variables connect(&m_delayedShowTimer, SIGNAL(timeout()), this, SLOT(show())); connect(Workspace::self(), SIGNAL(configChanged()), this, SLOT(reconfigure())); + + if (kwinApp()->operationMode() == Application::OperationModeX11) { + m_x11EventFilter.reset(new X11Filter); + } } TabBox::~TabBox() @@ -903,47 +908,6 @@ void TabBox::delayedShow() m_delayedShowTimer.start(m_delayShowTime); } -bool TabBox::handleMouseEvent(xcb_button_press_event_t *e) -{ - xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, XCB_CURRENT_TIME); - if (!m_isShown && isDisplayed()) { - // tabbox has been replaced, check effects - if (effects && static_cast(effects)->checkInputWindowEvent(e)) - return true; - } - if ((e->response_type & ~0x80) == XCB_BUTTON_PRESS) { - // press outside Tabbox? - QPoint pos(e->root_x, e->root_y); - - if ((!m_isShown && isDisplayed()) - || (!m_tabBox->containsPos(pos) && - (e->detail == XCB_BUTTON_INDEX_1 || e->detail == XCB_BUTTON_INDEX_2 || e->detail == XCB_BUTTON_INDEX_3))) { - close(); // click outside closes tab - return true; - } - if (e->detail == XCB_BUTTON_INDEX_5 || e->detail == XCB_BUTTON_INDEX_4) { - // mouse wheel event - const QModelIndex index = m_tabBox->nextPrev(e->detail == XCB_BUTTON_INDEX_5); - if (index.isValid()) { - setCurrentIndex(index); - } - return true; - } - } - return false; -} - -bool TabBox::handleMouseEvent(xcb_motion_notify_event_t *e) -{ - xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, XCB_CURRENT_TIME); - if (!m_isShown && isDisplayed()) { - // tabbox has been replaced, check effects - if (effects && static_cast(effects)->checkInputWindowEvent(e)) - return true; - } - return false; -} - bool TabBox::handleMouseEvent(QMouseEvent *event) { if (!m_isShown && isDisplayed()) { @@ -1539,70 +1503,6 @@ void TabBox::accept(bool closeTabBox) } } -/*! - Handles alt-tab / control-tab releasing - */ -void TabBox::keyRelease(const xcb_key_release_event_t *ev) -{ - if (m_noModifierGrab) { - return; - } - unsigned int mk = ev->state & - (KKeyServer::modXShift() | - KKeyServer::modXCtrl() | - KKeyServer::modXAlt() | - KKeyServer::modXMeta()); - // ev.state is state before the key release, so just checking mk being 0 isn't enough - // using XQueryPointer() also doesn't seem to work well, so the check that all - // modifiers are released: only one modifier is active and the currently released - // key is this modifier - if yes, release the grab - int mod_index = -1; - for (int i = XCB_MAP_INDEX_SHIFT; - i <= XCB_MAP_INDEX_5; - ++i) - if ((mk & (1 << i)) != 0) { - if (mod_index >= 0) - return; - mod_index = i; - } - bool release = false; - if (mod_index == -1) - release = true; - else { - Xcb::ModifierMapping xmk; - if (xmk) { - xcb_keycode_t *keycodes = xmk.keycodes(); - const int maxIndex = xmk.size(); - for (int i = 0; i < xmk->keycodes_per_modifier; ++i) { - const int index = xmk->keycodes_per_modifier * mod_index + i; - if (index >= maxIndex) { - continue; - } - if (keycodes[index] == ev->detail) { - release = true; - } - } - } - } - if (!release) - return; - if (m_tabGrab) { - bool old_control_grab = m_desktopGrab; - accept(); - m_desktopGrab = old_control_grab; - } - if (m_desktopGrab) { - bool old_tab_grab = m_tabGrab; - int desktop = currentDesktop(); - close(); - m_tabGrab = old_tab_grab; - if (desktop != -1) { - setCurrentDesktop(desktop); - VirtualDesktopManager::self()->setCurrent(desktop); - } - } -} - void TabBox::modifiersReleased() { if (m_noModifierGrab) { diff --git a/tabbox/tabbox.h b/tabbox/tabbox.h index 3cb8c7cf76..e4b1bbe55f 100644 --- a/tabbox/tabbox.h +++ b/tabbox/tabbox.h @@ -35,15 +35,13 @@ class QMouseEvent; class QKeyEvent; class QWheelEvent; -struct xcb_button_press_event_t; -struct xcb_motion_notify_event_t; - namespace KWin { class Workspace; class AbstractClient; class Client; +class X11EventFilter; namespace TabBox { class DesktopChainManager; @@ -164,8 +162,13 @@ public: return m_displayRefcount > 0; }; - bool handleMouseEvent(xcb_button_press_event_t *e); - bool handleMouseEvent(xcb_motion_notify_event_t *e); + /** + * @returns @c true if TabBox is shown, @c false if replaced by Effect + **/ + bool isShown() const { + return m_isShown; + } + bool handleMouseEvent(QMouseEvent *event); bool handleWheelEvent(QWheelEvent *event); void grabbedKeyEvent(QKeyEvent* event); @@ -181,7 +184,6 @@ public: int nextDesktopStatic(int iDesktop) const; int previousDesktopStatic(int iDesktop) const; void keyPress(int key); - void keyRelease(const xcb_key_release_event_t *ev); void modifiersReleased(); bool forcedGlobalMouseGrab() const { @@ -191,6 +193,7 @@ public: bool noModifierGrab() const { return m_noModifierGrab; } + void setCurrentIndex(QModelIndex index, bool notifyEffects = true); static TabBox *self(); static TabBox *create(QObject *parent); @@ -224,7 +227,6 @@ Q_SIGNALS: private: explicit TabBox(QObject *parent); - void setCurrentIndex(QModelIndex index, bool notifyEffects = true); void loadConfig(const KConfigGroup& config, TabBoxConfig& tabBoxConfig); bool startKDEWalkThroughWindows(TabBoxMode mode); // TabBoxWindowsMode | TabBoxWindowsAlternativeMode @@ -285,6 +287,7 @@ private: QList m_borderActivate, m_borderAlternativeActivate; QHash m_touchActivate; QHash m_touchAlternativeActivate; + QScopedPointer m_x11EventFilter; static TabBox *s_self; }; diff --git a/tabbox/x11_filter.cpp b/tabbox/x11_filter.cpp new file mode 100644 index 0000000000..337ed4700c --- /dev/null +++ b/tabbox/x11_filter.cpp @@ -0,0 +1,173 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "x11_filter.h" + +#include "effects.h" +#include "screenedge.h" +#include "tabbox/tabbox.h" + +#include + +#include + +namespace KWin +{ +namespace TabBox +{ + +X11Filter::X11Filter() + : X11EventFilter(QVector{XCB_KEY_PRESS, XCB_KEY_RELEASE, XCB_MOTION_NOTIFY, XCB_BUTTON_PRESS, XCB_BUTTON_RELEASE}) +{ +} + +template +static bool passToEffects(T *e) +{ + const auto tab = TabBox::TabBox::self(); + if (!tab->isShown() && tab->isDisplayed()) { + if (effects && static_cast(effects)->checkInputWindowEvent(e)) { + return true; + } + } + return false; +} + +bool X11Filter::event(xcb_generic_event_t *event) +{ + if (!TabBox::TabBox::self()->isGrabbed()) { + return false; + } + const uint8_t eventType = event->response_type & ~0x80; + switch (eventType) { + case XCB_BUTTON_PRESS: + case XCB_BUTTON_RELEASE: { + auto e = reinterpret_cast(event); + xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, XCB_CURRENT_TIME); + if (passToEffects(e)) { + return true; + } + if (eventType == XCB_BUTTON_PRESS) { + return buttonPress(e); + } + return false; + } + case XCB_MOTION_NOTIFY: { + return motion(event); + } + case XCB_KEY_PRESS: { + keyPress(event); + return true; + } + case XCB_KEY_RELEASE: + keyRelease(event); + return true; + } + return false; +} +bool X11Filter::buttonPress(xcb_button_press_event_t *event) +{ + // press outside Tabbox? + const auto tab = TabBox::TabBox::self(); + QPoint pos(event->root_x, event->root_y); + if ((!tab->isShown() && tab->isDisplayed()) + || (!tabBox->containsPos(pos) && + (event->detail == XCB_BUTTON_INDEX_1 || event->detail == XCB_BUTTON_INDEX_2 || event->detail == XCB_BUTTON_INDEX_3))) { + tab->close(); // click outside closes tab + return true; + } + if (event->detail == XCB_BUTTON_INDEX_5 || event->detail == XCB_BUTTON_INDEX_4) { + // mouse wheel event + const QModelIndex index = tabBox->nextPrev(event->detail == XCB_BUTTON_INDEX_5); + if (index.isValid()) { + tab->setCurrentIndex(index); + } + return true; + } + return false; +} + +bool X11Filter::motion(xcb_generic_event_t *event) +{ + auto *mouseEvent = reinterpret_cast(event); + const QPoint rootPos(mouseEvent->root_x, mouseEvent->root_y); + // TODO: this should be in ScreenEdges directly + ScreenEdges::self()->check(rootPos, QDateTime::fromMSecsSinceEpoch(xTime()), true); + xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, XCB_CURRENT_TIME); + if (passToEffects(mouseEvent)) { + return true; + } + return false; +} + +void X11Filter::keyPress(xcb_generic_event_t *event) +{ + int keyQt; + xcb_key_press_event_t *keyEvent = reinterpret_cast(event); + KKeyServer::xcbKeyPressEventToQt(keyEvent, &keyQt); + TabBox::TabBox::self()->keyPress(keyQt); +} + +void X11Filter::keyRelease(xcb_generic_event_t *event) +{ + const auto ev = reinterpret_cast(event); + unsigned int mk = ev->state & + (KKeyServer::modXShift() | + KKeyServer::modXCtrl() | + KKeyServer::modXAlt() | + KKeyServer::modXMeta()); + // ev.state is state before the key release, so just checking mk being 0 isn't enough + // using XQueryPointer() also doesn't seem to work well, so the check that all + // modifiers are released: only one modifier is active and the currently released + // key is this modifier - if yes, release the grab + int mod_index = -1; + for (int i = XCB_MAP_INDEX_SHIFT; + i <= XCB_MAP_INDEX_5; + ++i) + if ((mk & (1 << i)) != 0) { + if (mod_index >= 0) + return; + mod_index = i; + } + bool release = false; + if (mod_index == -1) + release = true; + else { + Xcb::ModifierMapping xmk; + if (xmk) { + xcb_keycode_t *keycodes = xmk.keycodes(); + const int maxIndex = xmk.size(); + for (int i = 0; i < xmk->keycodes_per_modifier; ++i) { + const int index = xmk->keycodes_per_modifier * mod_index + i; + if (index >= maxIndex) { + continue; + } + if (keycodes[index] == ev->detail) { + release = true; + } + } + } + } + if (release) { + TabBox::TabBox::self()->modifiersReleased(); + } +} + +} +} diff --git a/tabbox/x11_filter.h b/tabbox/x11_filter.h new file mode 100644 index 0000000000..10154fc594 --- /dev/null +++ b/tabbox/x11_filter.h @@ -0,0 +1,49 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_TABBOX_X11_FILTER_H +#define KWIN_TABBOX_X11_FILTER_H +#include "x11eventfilter.h" + +namespace KWin +{ +namespace TabBox +{ + +class X11Filter : public X11EventFilter +{ +public: + explicit X11Filter(); + + bool event(xcb_generic_event_t *event) override; + +private: + bool buttonPress(xcb_button_press_event_t *event); + bool motion(xcb_generic_event_t *event); + void keyPress(xcb_generic_event_t *event); + void keyRelease(xcb_generic_event_t *event); +}; + +} + +} + +#endif + +