File indexing completed on 2024-05-19 16:34:56

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "x11_filter.h"
0010 
0011 #include "effects.h"
0012 #include "screenedge.h"
0013 #include "tabbox/tabbox.h"
0014 #include "workspace.h"
0015 
0016 #include <KKeyServer>
0017 
0018 #include <xcb/xcb.h>
0019 
0020 namespace KWin
0021 {
0022 namespace TabBox
0023 {
0024 
0025 X11Filter::X11Filter()
0026     : X11EventFilter(QVector<int>{XCB_KEY_PRESS, XCB_KEY_RELEASE, XCB_MOTION_NOTIFY, XCB_BUTTON_PRESS, XCB_BUTTON_RELEASE})
0027 {
0028 }
0029 
0030 bool X11Filter::event(xcb_generic_event_t *event)
0031 {
0032     const auto tab = workspace()->tabbox();
0033     if (!tab->isGrabbed()) {
0034         return false;
0035     }
0036     const uint8_t eventType = event->response_type & ~0x80;
0037     switch (eventType) {
0038     case XCB_BUTTON_PRESS:
0039     case XCB_BUTTON_RELEASE: {
0040         auto e = reinterpret_cast<xcb_button_press_event_t *>(event);
0041         xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, XCB_CURRENT_TIME);
0042         if (!tab->isShown() && tab->isDisplayed()) {
0043             if (effects && static_cast<EffectsHandlerImpl *>(effects)->isMouseInterception()) {
0044                 // pass on to effects, effects will filter out the event
0045                 return false;
0046             }
0047         }
0048         if (eventType == XCB_BUTTON_PRESS) {
0049             return buttonPress(e);
0050         }
0051         return false;
0052     }
0053     case XCB_MOTION_NOTIFY: {
0054         motion(event);
0055         break;
0056     }
0057     case XCB_KEY_PRESS: {
0058         keyPress(event);
0059         return true;
0060     }
0061     case XCB_KEY_RELEASE:
0062         keyRelease(event);
0063         return true;
0064     }
0065     return false;
0066 }
0067 bool X11Filter::buttonPress(xcb_button_press_event_t *event)
0068 {
0069     // press outside Tabbox?
0070     const auto tab = workspace()->tabbox();
0071     QPoint pos(event->root_x, event->root_y);
0072     if ((!tab->isShown() && tab->isDisplayed())
0073         || (!tabBox->containsPos(pos) && (event->detail == XCB_BUTTON_INDEX_1 || event->detail == XCB_BUTTON_INDEX_2 || event->detail == XCB_BUTTON_INDEX_3))) {
0074         tab->close(); // click outside closes tab
0075         return true;
0076     }
0077     if (event->detail == XCB_BUTTON_INDEX_5 || event->detail == XCB_BUTTON_INDEX_4) {
0078         // mouse wheel event
0079         const QModelIndex index = tabBox->nextPrev(event->detail == XCB_BUTTON_INDEX_5);
0080         if (index.isValid()) {
0081             tab->setCurrentIndex(index);
0082         }
0083         return true;
0084     }
0085     return false;
0086 }
0087 
0088 void X11Filter::motion(xcb_generic_event_t *event)
0089 {
0090     auto *mouseEvent = reinterpret_cast<xcb_motion_notify_event_t *>(event);
0091     const QPoint rootPos(mouseEvent->root_x, mouseEvent->root_y);
0092     // TODO: this should be in ScreenEdges directly
0093     workspace()->screenEdges()->check(rootPos, QDateTime::fromMSecsSinceEpoch(xTime(), Qt::UTC), true);
0094     xcb_allow_events(connection(), XCB_ALLOW_ASYNC_POINTER, XCB_CURRENT_TIME);
0095 }
0096 
0097 void X11Filter::keyPress(xcb_generic_event_t *event)
0098 {
0099     int keyQt;
0100     xcb_key_press_event_t *keyEvent = reinterpret_cast<xcb_key_press_event_t *>(event);
0101     KKeyServer::xcbKeyPressEventToQt(keyEvent, &keyQt);
0102     workspace()->tabbox()->keyPress(keyQt);
0103 }
0104 
0105 void X11Filter::keyRelease(xcb_generic_event_t *event)
0106 {
0107     const auto ev = reinterpret_cast<xcb_key_release_event_t *>(event);
0108     unsigned int mk = ev->state & (KKeyServer::modXShift() | KKeyServer::modXCtrl() | KKeyServer::modXAlt() | KKeyServer::modXMeta());
0109     // ev.state is state before the key release, so just checking mk being 0 isn't enough
0110     // using XQueryPointer() also doesn't seem to work well, so the check that all
0111     // modifiers are released: only one modifier is active and the currently released
0112     // key is this modifier - if yes, release the grab
0113     int mod_index = -1;
0114     for (int i = XCB_MAP_INDEX_SHIFT;
0115          i <= XCB_MAP_INDEX_5;
0116          ++i) {
0117         if ((mk & (1 << i)) != 0) {
0118             if (mod_index >= 0) {
0119                 return;
0120             }
0121             mod_index = i;
0122         }
0123     }
0124     bool release = false;
0125     if (mod_index == -1) {
0126         release = true;
0127     } else {
0128         Xcb::ModifierMapping xmk;
0129         if (xmk) {
0130             xcb_keycode_t *keycodes = xmk.keycodes();
0131             const int maxIndex = xmk.size();
0132             for (int i = 0; i < xmk->keycodes_per_modifier; ++i) {
0133                 const int index = xmk->keycodes_per_modifier * mod_index + i;
0134                 if (index >= maxIndex) {
0135                     continue;
0136                 }
0137                 if (keycodes[index] == ev->detail) {
0138                     release = true;
0139                 }
0140             }
0141         }
0142     }
0143     if (release) {
0144         workspace()->tabbox()->modifiersReleased();
0145     }
0146 }
0147 
0148 }
0149 }