File indexing completed on 2024-04-28 05:30:16

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
0006     SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 /*
0012 
0013  This file contains things relevant to handling incoming events.
0014 
0015 */
0016 
0017 #include "atoms.h"
0018 #include "cursor.h"
0019 #include "effect/effecthandler.h"
0020 #include "focuschain.h"
0021 #include "group.h"
0022 #include "netinfo.h"
0023 #include "rules.h"
0024 #include "screenedge.h"
0025 #include "useractions.h"
0026 #include "utils/xcbutils.h"
0027 #include "wayland/surface.h"
0028 #include "wayland/xwaylandshell_v1.h"
0029 #include "wayland_server.h"
0030 #include "workspace.h"
0031 #include "x11window.h"
0032 
0033 #if KWIN_BUILD_TABBOX
0034 #include "tabbox/tabbox.h"
0035 #endif
0036 
0037 #include <KDecoration2/Decoration>
0038 
0039 #include <QApplication>
0040 #include <QDebug>
0041 #include <QHoverEvent>
0042 #include <QKeyEvent>
0043 #include <QMouseEvent>
0044 #include <QStyleHints>
0045 #include <QWheelEvent>
0046 
0047 #include <kkeyserver.h>
0048 
0049 #include <xcb/damage.h>
0050 #include <xcb/sync.h>
0051 #include <xcb/xcb_icccm.h>
0052 
0053 #include "compositor.h"
0054 #include "x11eventfilter.h"
0055 
0056 #ifndef XCB_GE_GENERIC
0057 #define XCB_GE_GENERIC 35
0058 typedef struct xcb_ge_generic_event_t
0059 {
0060     uint8_t response_type; /**<  */
0061     uint8_t extension; /**<  */
0062     uint16_t sequence; /**<  */
0063     uint32_t length; /**<  */
0064     uint16_t event_type; /**<  */
0065     uint8_t pad0[22]; /**<  */
0066     uint32_t full_sequence; /**<  */
0067 } xcb_ge_generic_event_t;
0068 #endif
0069 
0070 namespace KWin
0071 {
0072 
0073 // ****************************************
0074 // Workspace
0075 // ****************************************
0076 
0077 static xcb_window_t findEventWindow(xcb_generic_event_t *event)
0078 {
0079     const uint8_t eventType = event->response_type & ~0x80;
0080     switch (eventType) {
0081     case XCB_KEY_PRESS:
0082     case XCB_KEY_RELEASE:
0083         return reinterpret_cast<xcb_key_press_event_t *>(event)->event;
0084     case XCB_BUTTON_PRESS:
0085     case XCB_BUTTON_RELEASE:
0086         return reinterpret_cast<xcb_button_press_event_t *>(event)->event;
0087     case XCB_MOTION_NOTIFY:
0088         return reinterpret_cast<xcb_motion_notify_event_t *>(event)->event;
0089     case XCB_ENTER_NOTIFY:
0090     case XCB_LEAVE_NOTIFY:
0091         return reinterpret_cast<xcb_enter_notify_event_t *>(event)->event;
0092     case XCB_FOCUS_IN:
0093     case XCB_FOCUS_OUT:
0094         return reinterpret_cast<xcb_focus_in_event_t *>(event)->event;
0095     case XCB_EXPOSE:
0096         return reinterpret_cast<xcb_expose_event_t *>(event)->window;
0097     case XCB_GRAPHICS_EXPOSURE:
0098         return reinterpret_cast<xcb_graphics_exposure_event_t *>(event)->drawable;
0099     case XCB_NO_EXPOSURE:
0100         return reinterpret_cast<xcb_no_exposure_event_t *>(event)->drawable;
0101     case XCB_VISIBILITY_NOTIFY:
0102         return reinterpret_cast<xcb_visibility_notify_event_t *>(event)->window;
0103     case XCB_CREATE_NOTIFY:
0104         return reinterpret_cast<xcb_create_notify_event_t *>(event)->window;
0105     case XCB_DESTROY_NOTIFY:
0106         return reinterpret_cast<xcb_destroy_notify_event_t *>(event)->window;
0107     case XCB_UNMAP_NOTIFY:
0108         return reinterpret_cast<xcb_unmap_notify_event_t *>(event)->window;
0109     case XCB_MAP_NOTIFY:
0110         return reinterpret_cast<xcb_map_notify_event_t *>(event)->window;
0111     case XCB_MAP_REQUEST:
0112         return reinterpret_cast<xcb_map_request_event_t *>(event)->window;
0113     case XCB_REPARENT_NOTIFY:
0114         return reinterpret_cast<xcb_reparent_notify_event_t *>(event)->window;
0115     case XCB_CONFIGURE_NOTIFY:
0116         return reinterpret_cast<xcb_configure_notify_event_t *>(event)->window;
0117     case XCB_CONFIGURE_REQUEST:
0118         return reinterpret_cast<xcb_configure_request_event_t *>(event)->window;
0119     case XCB_GRAVITY_NOTIFY:
0120         return reinterpret_cast<xcb_gravity_notify_event_t *>(event)->window;
0121     case XCB_RESIZE_REQUEST:
0122         return reinterpret_cast<xcb_resize_request_event_t *>(event)->window;
0123     case XCB_CIRCULATE_NOTIFY:
0124     case XCB_CIRCULATE_REQUEST:
0125         return reinterpret_cast<xcb_circulate_notify_event_t *>(event)->window;
0126     case XCB_PROPERTY_NOTIFY:
0127         return reinterpret_cast<xcb_property_notify_event_t *>(event)->window;
0128     case XCB_COLORMAP_NOTIFY:
0129         return reinterpret_cast<xcb_colormap_notify_event_t *>(event)->window;
0130     case XCB_CLIENT_MESSAGE:
0131         return reinterpret_cast<xcb_client_message_event_t *>(event)->window;
0132     default:
0133         // extension handling
0134         if (eventType == Xcb::Extensions::self()->shapeNotifyEvent()) {
0135             return reinterpret_cast<xcb_shape_notify_event_t *>(event)->affected_window;
0136         }
0137         if (eventType == Xcb::Extensions::self()->damageNotifyEvent()) {
0138             return reinterpret_cast<xcb_damage_notify_event_t *>(event)->drawable;
0139         }
0140         return XCB_WINDOW_NONE;
0141     }
0142 }
0143 
0144 /**
0145  * Handles workspace specific XCB event
0146  */
0147 bool Workspace::workspaceEvent(xcb_generic_event_t *e)
0148 {
0149     const uint8_t eventType = e->response_type & ~0x80;
0150 
0151     const xcb_window_t eventWindow = findEventWindow(e);
0152     if (eventWindow != XCB_WINDOW_NONE) {
0153         if (X11Window *window = findClient(Predicate::WindowMatch, eventWindow)) {
0154             if (window->windowEvent(e)) {
0155                 return true;
0156             }
0157         } else if (X11Window *window = findClient(Predicate::WrapperIdMatch, eventWindow)) {
0158             if (window->windowEvent(e)) {
0159                 return true;
0160             }
0161         } else if (X11Window *window = findClient(Predicate::FrameIdMatch, eventWindow)) {
0162             if (window->windowEvent(e)) {
0163                 return true;
0164             }
0165         } else if (X11Window *window = findClient(Predicate::InputIdMatch, eventWindow)) {
0166             if (window->windowEvent(e)) {
0167                 return true;
0168             }
0169         } else if (X11Window *window = findUnmanaged(eventWindow)) {
0170             if (window->windowEvent(e)) {
0171                 return true;
0172             }
0173         }
0174     }
0175 
0176     switch (eventType) {
0177     case XCB_CREATE_NOTIFY: {
0178         const auto *event = reinterpret_cast<xcb_create_notify_event_t *>(e);
0179         if (event->parent == kwinApp()->x11RootWindow() && !QWidget::find(event->window) && !event->override_redirect) {
0180             // see comments for allowWindowActivation()
0181             kwinApp()->updateXTime();
0182             const xcb_timestamp_t t = xTime();
0183             xcb_change_property(kwinApp()->x11Connection(), XCB_PROP_MODE_REPLACE, event->window, atoms->kde_net_wm_user_creation_time, XCB_ATOM_CARDINAL, 32, 1, &t);
0184         }
0185         break;
0186     }
0187     case XCB_UNMAP_NOTIFY: {
0188         const auto *event = reinterpret_cast<xcb_unmap_notify_event_t *>(e);
0189         return (event->event != event->window); // hide wm typical event from Qt
0190     }
0191     case XCB_REPARENT_NOTIFY: {
0192         // do not confuse Qt with these events. After all, _we_ are the
0193         // window manager who does the reparenting.
0194         return true;
0195     }
0196     case XCB_MAP_REQUEST: {
0197         kwinApp()->updateXTime();
0198 
0199         const auto *event = reinterpret_cast<xcb_map_request_event_t *>(e);
0200         if (X11Window *window = findClient(Predicate::WindowMatch, event->window)) {
0201             // e->xmaprequest.window is different from e->xany.window
0202             // TODO this shouldn't be necessary now
0203             window->windowEvent(e);
0204             m_focusChain->update(window, FocusChain::Update);
0205         } else if (true /*|| e->xmaprequest.parent != root */) {
0206             // NOTICE don't check for the parent being the root window, this breaks when some app unmaps
0207             // a window, changes something and immediately maps it back, without giving KWin
0208             // a chance to reparent it back to root
0209             // since KWin can get MapRequest only for root window children and
0210             // children of WindowWrapper (=clients), the check is AFAIK useless anyway
0211             // NOTICE: The save-set support in X11Window::mapRequestEvent() actually requires that
0212             // this code doesn't check the parent to be root.
0213             if (!createX11Window(event->window, false)) {
0214                 xcb_map_window(kwinApp()->x11Connection(), event->window);
0215                 const uint32_t values[] = {XCB_STACK_MODE_ABOVE};
0216                 xcb_configure_window(kwinApp()->x11Connection(), event->window, XCB_CONFIG_WINDOW_STACK_MODE, values);
0217             }
0218         }
0219         return true;
0220     }
0221     case XCB_MAP_NOTIFY: {
0222         const auto *event = reinterpret_cast<xcb_map_notify_event_t *>(e);
0223         if (event->override_redirect) {
0224             X11Window *window = findUnmanaged(event->window);
0225             if (window == nullptr) {
0226                 window = createUnmanaged(event->window);
0227             }
0228             if (window) {
0229                 // if hasScheduledRelease is true, it means a unamp and map sequence has occurred.
0230                 // since release is scheduled after map notify, this old Unmanaged will get released
0231                 // before KWIN has chance to remanage it again. so release it right now.
0232                 if (window->hasScheduledRelease()) {
0233                     window->releaseWindow();
0234                     window = createUnmanaged(event->window);
0235                 }
0236                 if (window) {
0237                     return window->windowEvent(e);
0238                 }
0239             }
0240         }
0241         return (event->event != event->window); // hide wm typical event from Qt
0242     }
0243 
0244     case XCB_CONFIGURE_REQUEST: {
0245         const auto *event = reinterpret_cast<xcb_configure_request_event_t *>(e);
0246         if (event->parent == kwinApp()->x11RootWindow()) {
0247             uint32_t values[5] = {0, 0, 0, 0, 0};
0248             const uint32_t value_mask = event->value_mask
0249                 & (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_BORDER_WIDTH);
0250             int i = 0;
0251             if (value_mask & XCB_CONFIG_WINDOW_X) {
0252                 values[i++] = event->x;
0253             }
0254             if (value_mask & XCB_CONFIG_WINDOW_Y) {
0255                 values[i++] = event->y;
0256             }
0257             if (value_mask & XCB_CONFIG_WINDOW_WIDTH) {
0258                 values[i++] = event->width;
0259             }
0260             if (value_mask & XCB_CONFIG_WINDOW_HEIGHT) {
0261                 values[i++] = event->height;
0262             }
0263             if (value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
0264                 values[i++] = event->border_width;
0265             }
0266             xcb_configure_window(kwinApp()->x11Connection(), event->window, value_mask, values);
0267             return true;
0268         }
0269         break;
0270     }
0271     case XCB_CONFIGURE_NOTIFY: {
0272         const auto configureNotifyEvent = reinterpret_cast<xcb_configure_notify_event_t *>(e);
0273         if (configureNotifyEvent->override_redirect && configureNotifyEvent->event == kwinApp()->x11RootWindow()) {
0274             updateXStackingOrder();
0275         }
0276         break;
0277     }
0278     case XCB_FOCUS_IN: {
0279         const auto *event = reinterpret_cast<xcb_focus_in_event_t *>(e);
0280         if (event->event == kwinApp()->x11RootWindow()
0281             && (event->detail == XCB_NOTIFY_DETAIL_NONE || event->detail == XCB_NOTIFY_DETAIL_POINTER_ROOT || event->detail == XCB_NOTIFY_DETAIL_INFERIOR)) {
0282             Xcb::CurrentInput currentInput;
0283             kwinApp()->updateXTime(); // focusToNull() uses xTime(), which is old now (FocusIn has no timestamp)
0284             // it seems we can "loose" focus reversions when the closing window hold a grab
0285             // => catch the typical pattern (though we don't want the focus on the root anyway) #348935
0286             const bool lostFocusPointerToRoot = currentInput->focus == kwinApp()->x11RootWindow() && event->detail == XCB_NOTIFY_DETAIL_INFERIOR;
0287             if (!currentInput.isNull() && (currentInput->focus == XCB_WINDOW_NONE || currentInput->focus == XCB_INPUT_FOCUS_POINTER_ROOT || lostFocusPointerToRoot)) {
0288                 // kWarning( 1212 ) << "X focus set to None/PointerRoot, reseting focus" ;
0289                 Window *window = mostRecentlyActivatedWindow();
0290                 if (window != nullptr) {
0291                     requestFocus(window, true);
0292                 } else if (activateNextWindow(nullptr)) {
0293                     ; // ok, activated
0294                 } else {
0295                     focusToNull();
0296                 }
0297             }
0298         }
0299     }
0300         // fall through
0301     case XCB_FOCUS_OUT:
0302         return true; // always eat these, they would tell Qt that KWin is the active app
0303     default:
0304         break;
0305     }
0306     return false;
0307 }
0308 
0309 // ****************************************
0310 // Client
0311 // ****************************************
0312 
0313 /**
0314  * General handler for XEvents concerning the client window
0315  */
0316 bool X11Window::windowEvent(xcb_generic_event_t *e)
0317 {
0318     if (isUnmanaged()) {
0319         NET::Properties dirtyProperties;
0320         NET::Properties2 dirtyProperties2;
0321         info->event(e, &dirtyProperties, &dirtyProperties2); // pass through the NET stuff
0322         if (dirtyProperties2 & NET::WM2Opacity) {
0323             if (Compositor::compositing()) {
0324                 setOpacity(info->opacityF());
0325             }
0326         }
0327         if (dirtyProperties2 & NET::WM2OpaqueRegion) {
0328             getWmOpaqueRegion();
0329         }
0330         if (dirtyProperties2.testFlag(NET::WM2WindowRole)) {
0331             Q_EMIT windowRoleChanged();
0332         }
0333         if (dirtyProperties2.testFlag(NET::WM2WindowClass)) {
0334             getResourceClass();
0335         }
0336         const uint8_t eventType = e->response_type & ~0x80;
0337         switch (eventType) {
0338         case XCB_DESTROY_NOTIFY:
0339             destroyWindow();
0340             break;
0341         case XCB_UNMAP_NOTIFY: {
0342             workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event
0343 
0344             // unmap notify might have been emitted due to a destroy notify
0345             // but unmap notify gets emitted before the destroy notify, nevertheless at this
0346             // point the window is already destroyed. This means any XCB request with the window
0347             // will cause an error.
0348             // To not run into these errors we try to wait for the destroy notify. For this we
0349             // generate a round trip to the X server and wait a very short time span before
0350             // handling the release.
0351             kwinApp()->updateXTime();
0352             // using 1 msec to not just move it at the end of the event loop but add an very short
0353             // timespan to cover cases like unmap() followed by destroy(). The only other way to
0354             // ensure that the window is not destroyed when we do the release handling is to grab
0355             // the XServer which we do not want to do for an Unmanaged. The timespan of 1 msec is
0356             // short enough to not cause problems in the close window animations.
0357             // It's of course still possible that we miss the destroy in which case non-fatal
0358             // X errors are reported to the event loop and logged by Qt.
0359             m_releaseTimer.start(1);
0360             break;
0361         }
0362         case XCB_CONFIGURE_NOTIFY:
0363             configureNotifyEvent(reinterpret_cast<xcb_configure_notify_event_t *>(e));
0364             break;
0365         case XCB_PROPERTY_NOTIFY:
0366             propertyNotifyEvent(reinterpret_cast<xcb_property_notify_event_t *>(e));
0367             break;
0368         case XCB_CLIENT_MESSAGE:
0369             clientMessageEvent(reinterpret_cast<xcb_client_message_event_t *>(e));
0370             break;
0371         default: {
0372             if (eventType == Xcb::Extensions::self()->shapeNotifyEvent()) {
0373                 detectShape();
0374                 Q_EMIT shapeChanged();
0375             }
0376             if (eventType == Xcb::Extensions::self()->damageNotifyEvent()) {
0377                 damageNotifyEvent();
0378             }
0379             break;
0380         }
0381         }
0382         return false; // don't eat events, even our own unmanaged widgets are tracked
0383     }
0384 
0385     if (findEventWindow(e) == window()) { // avoid doing stuff on frame or wrapper
0386         NET::Properties dirtyProperties;
0387         NET::Properties2 dirtyProperties2;
0388         info->event(e, &dirtyProperties, &dirtyProperties2); // pass through the NET stuff
0389 
0390         if ((dirtyProperties & NET::WMName) != 0) {
0391             fetchName();
0392         }
0393         if ((dirtyProperties & NET::WMIconName) != 0) {
0394             fetchIconicName();
0395         }
0396         if ((dirtyProperties & NET::WMStrut) != 0
0397             || (dirtyProperties2 & NET::WM2ExtendedStrut) != 0) {
0398             workspace()->updateClientArea();
0399         }
0400         if ((dirtyProperties & NET::WMIcon) != 0) {
0401             getIcons();
0402         }
0403         // Note there's a difference between userTime() and info->userTime()
0404         // info->userTime() is the value of the property, userTime() also includes
0405         // updates of the time done by KWin (ButtonPress on windowrapper etc.).
0406         if ((dirtyProperties2 & NET::WM2UserTime) != 0) {
0407             workspace()->setWasUserInteraction();
0408             updateUserTime(info->userTime());
0409         }
0410         if ((dirtyProperties2 & NET::WM2StartupId) != 0) {
0411             startupIdChanged();
0412         }
0413         if (dirtyProperties2 & NET::WM2Opacity) {
0414             if (Compositor::compositing()) {
0415                 setOpacity(info->opacityF());
0416             } else {
0417                 // forward to the frame if there's possibly another compositing manager running
0418                 NETWinInfo i(kwinApp()->x11Connection(), frameId(), kwinApp()->x11RootWindow(), NET::Properties(), NET::Properties2());
0419                 i.setOpacity(info->opacity());
0420             }
0421         }
0422         if (dirtyProperties2.testFlag(NET::WM2WindowRole)) {
0423             Q_EMIT windowRoleChanged();
0424         }
0425         if (dirtyProperties2.testFlag(NET::WM2WindowClass)) {
0426             getResourceClass();
0427         }
0428         if (dirtyProperties2.testFlag(NET::WM2BlockCompositing)) {
0429             setBlockingCompositing(info->isBlockingCompositing());
0430         }
0431         if (dirtyProperties2.testFlag(NET::WM2GroupLeader)) {
0432             checkGroup();
0433             updateAllowedActions(); // Group affects isMinimizable()
0434         }
0435         if (dirtyProperties2.testFlag(NET::WM2Urgency)) {
0436             updateUrgency();
0437         }
0438         if (dirtyProperties2 & NET::WM2OpaqueRegion) {
0439             getWmOpaqueRegion();
0440         }
0441         if (dirtyProperties2 & NET::WM2DesktopFileName) {
0442             setDesktopFileName(QString::fromUtf8(info->desktopFileName()));
0443         }
0444         if (dirtyProperties2 & NET::WM2GTKFrameExtents) {
0445             setClientFrameExtents(info->gtkFrameExtents());
0446         }
0447     }
0448 
0449     const uint8_t eventType = e->response_type & ~0x80;
0450     switch (eventType) {
0451     case XCB_UNMAP_NOTIFY:
0452         unmapNotifyEvent(reinterpret_cast<xcb_unmap_notify_event_t *>(e));
0453         break;
0454     case XCB_DESTROY_NOTIFY:
0455         destroyNotifyEvent(reinterpret_cast<xcb_destroy_notify_event_t *>(e));
0456         break;
0457     case XCB_MAP_REQUEST:
0458         // this one may pass the event to workspace
0459         return mapRequestEvent(reinterpret_cast<xcb_map_request_event_t *>(e));
0460     case XCB_CONFIGURE_REQUEST:
0461         configureRequestEvent(reinterpret_cast<xcb_configure_request_event_t *>(e));
0462         break;
0463     case XCB_PROPERTY_NOTIFY:
0464         propertyNotifyEvent(reinterpret_cast<xcb_property_notify_event_t *>(e));
0465         break;
0466     case XCB_KEY_PRESS:
0467         updateUserTime(reinterpret_cast<xcb_key_press_event_t *>(e)->time);
0468         break;
0469     case XCB_BUTTON_PRESS: {
0470         const auto *event = reinterpret_cast<xcb_button_press_event_t *>(e);
0471         updateUserTime(event->time);
0472         buttonPressEvent(event->event, event->detail, event->state,
0473                          event->event_x, event->event_y, event->root_x, event->root_y, event->time);
0474         break;
0475     }
0476     case XCB_KEY_RELEASE:
0477         // don't update user time on releases
0478         // e.g. if the user presses Alt+F2, the Alt release
0479         // would appear as user input to the currently active window
0480         break;
0481     case XCB_BUTTON_RELEASE: {
0482         const auto *event = reinterpret_cast<xcb_button_release_event_t *>(e);
0483         // don't update user time on releases
0484         // e.g. if the user presses Alt+F2, the Alt release
0485         // would appear as user input to the currently active window
0486         buttonReleaseEvent(event->event, event->detail, event->state,
0487                            event->event_x, event->event_y, event->root_x, event->root_y);
0488         break;
0489     }
0490     case XCB_MOTION_NOTIFY: {
0491         const auto *event = reinterpret_cast<xcb_motion_notify_event_t *>(e);
0492 
0493         int x = Xcb::fromXNative(event->event_x);
0494         int y = Xcb::fromXNative(event->event_y);
0495         int root_x = Xcb::fromXNative(event->root_x);
0496         int root_y = Xcb::fromXNative(event->root_y);
0497 
0498         motionNotifyEvent(event->event, event->state,
0499                           x, y, root_x, root_y);
0500         workspace()->updateFocusMousePosition(QPointF(root_x, root_y));
0501         break;
0502     }
0503     case XCB_ENTER_NOTIFY: {
0504         auto *event = reinterpret_cast<xcb_enter_notify_event_t *>(e);
0505         enterNotifyEvent(event);
0506         // MotionNotify is guaranteed to be generated only if the mouse
0507         // move start and ends in the window; for cases when it only
0508         // starts or only ends there, Enter/LeaveNotify are generated.
0509         // Fake a MotionEvent in such cases to make handle of mouse
0510         // events simpler (Qt does that too).
0511         int x = Xcb::fromXNative(event->event_x);
0512         int y = Xcb::fromXNative(event->event_y);
0513         int root_x = Xcb::fromXNative(event->root_x);
0514         int root_y = Xcb::fromXNative(event->root_y);
0515 
0516         motionNotifyEvent(event->event, event->state,
0517                           x, y, root_x, root_y);
0518         workspace()->updateFocusMousePosition(QPointF(root_x, root_y));
0519         break;
0520     }
0521     case XCB_LEAVE_NOTIFY: {
0522         auto *event = reinterpret_cast<xcb_leave_notify_event_t *>(e);
0523 
0524         int x = Xcb::fromXNative(event->event_x);
0525         int y = Xcb::fromXNative(event->event_y);
0526         int root_x = Xcb::fromXNative(event->root_x);
0527         int root_y = Xcb::fromXNative(event->root_y);
0528         motionNotifyEvent(event->event, event->state,
0529                           x, y, root_x, root_y);
0530         leaveNotifyEvent(event);
0531         // not here, it'd break following enter notify handling
0532         // workspace()->updateFocusMousePosition( QPoint( e->xcrossing.x_root, e->xcrossing.y_root ));
0533         break;
0534     }
0535     case XCB_FOCUS_IN:
0536         focusInEvent(reinterpret_cast<xcb_focus_in_event_t *>(e));
0537         break;
0538     case XCB_FOCUS_OUT:
0539         focusOutEvent(reinterpret_cast<xcb_focus_out_event_t *>(e));
0540         break;
0541     case XCB_REPARENT_NOTIFY:
0542         break;
0543     case XCB_CLIENT_MESSAGE:
0544         clientMessageEvent(reinterpret_cast<xcb_client_message_event_t *>(e));
0545         break;
0546     case XCB_EXPOSE: {
0547         xcb_expose_event_t *event = reinterpret_cast<xcb_expose_event_t *>(e);
0548         if (event->window == frameId() && !Compositor::self()->isActive()) {
0549             // TODO: only repaint required areas
0550             triggerDecorationRepaint();
0551         }
0552         break;
0553     }
0554     default:
0555         if (eventType == Xcb::Extensions::self()->shapeNotifyEvent() && reinterpret_cast<xcb_shape_notify_event_t *>(e)->affected_window == window()) {
0556             detectShape(); // workaround for #19644
0557             updateShape();
0558         }
0559         if (eventType == Xcb::Extensions::self()->damageNotifyEvent() && reinterpret_cast<xcb_damage_notify_event_t *>(e)->drawable == frameId()) {
0560             damageNotifyEvent();
0561         }
0562         break;
0563     }
0564     return true; // eat all events
0565 }
0566 
0567 /**
0568  * Handles map requests of the client window
0569  */
0570 bool X11Window::mapRequestEvent(xcb_map_request_event_t *e)
0571 {
0572     if (e->window != window()) {
0573         // Special support for the save-set feature, which is a bit broken.
0574         // If there's a window from one client embedded in another one,
0575         // e.g. using XEMBED, and the embedder suddenly loses its X connection,
0576         // save-set will reparent the embedded window to its closest ancestor
0577         // that will remains. Unfortunately, with reparenting window managers,
0578         // this is not the root window, but the frame (or in KWin's case,
0579         // it's the wrapper for the client window). In this case,
0580         // the wrapper will get ReparentNotify for a window it won't know,
0581         // which will be ignored, and then it gets MapRequest, as save-set
0582         // always maps. Returning true here means that Workspace::workspaceEvent()
0583         // will handle this MapRequest and manage this window (i.e. act as if
0584         // it was reparented to root window).
0585         if (e->parent == wrapperId()) {
0586             return false;
0587         }
0588         return true; // no messing with frame etc.
0589     }
0590     // also copied in clientMessage()
0591     if (isMinimized()) {
0592         setMinimized(false);
0593     }
0594     if (isShade()) {
0595         setShade(ShadeNone);
0596     }
0597     if (!isOnCurrentDesktop()) {
0598         if (allowWindowActivation()) {
0599             workspace()->activateWindow(this);
0600         } else {
0601             demandAttention();
0602         }
0603     }
0604     return true;
0605 }
0606 
0607 /**
0608  * Handles unmap notify events of the client window
0609  */
0610 void X11Window::unmapNotifyEvent(xcb_unmap_notify_event_t *e)
0611 {
0612     if (e->window != window()) {
0613         return;
0614     }
0615     if (e->event != wrapperId()) {
0616         // most probably event from root window when initially reparenting
0617         bool ignore = true;
0618         if (e->event == kwinApp()->x11RootWindow() && (e->response_type & 0x80)) {
0619             ignore = false; // XWithdrawWindow()
0620         }
0621         if (ignore) {
0622             return;
0623         }
0624     }
0625 
0626     // check whether this is result of an XReparentWindow - window then won't be parented by wrapper
0627     // in this case do not release the window (causes reparent to root, removal from saveSet and what not)
0628     // but just destroy the window
0629     Xcb::Tree tree(m_client);
0630     xcb_window_t daddy = tree.parent();
0631     if (daddy == m_wrapper) {
0632         releaseWindow(); // unmapped from a regular window state
0633     } else {
0634         destroyWindow(); // the window was moved to some other parent
0635     }
0636 }
0637 
0638 void X11Window::destroyNotifyEvent(xcb_destroy_notify_event_t *e)
0639 {
0640     if (e->window != window()) {
0641         return;
0642     }
0643     destroyWindow();
0644 }
0645 
0646 /**
0647  * Handles client messages for the client window
0648  */
0649 void X11Window::clientMessageEvent(xcb_client_message_event_t *e)
0650 {
0651     if (e->type == atoms->wl_surface_serial) {
0652         m_surfaceSerial = (uint64_t(e->data.data32[1]) << 32) | e->data.data32[0];
0653         if (auto w = waylandServer()) {
0654             if (XwaylandSurfaceV1Interface *xwaylandSurface = w->xwaylandShell()->findSurface(m_surfaceSerial)) {
0655                 setSurface(xwaylandSurface->surface());
0656             }
0657         }
0658     } else if (e->type == atoms->wl_surface_id) {
0659         m_pendingSurfaceId = e->data.data32[0];
0660         if (auto w = waylandServer()) {
0661             if (auto s = SurfaceInterface::get(m_pendingSurfaceId, w->xWaylandConnection())) {
0662                 setSurface(s);
0663             }
0664         }
0665     }
0666 
0667     if (e->window != window()) {
0668         return; // ignore frame/wrapper
0669     }
0670     // WM_STATE
0671     if (e->type == atoms->wm_change_state) {
0672         if (e->data.data32[0] == XCB_ICCCM_WM_STATE_ICONIC) {
0673             setMinimized(true);
0674         }
0675         return;
0676     }
0677 }
0678 
0679 void X11Window::configureNotifyEvent(xcb_configure_notify_event_t *e)
0680 {
0681     if (effects) {
0682         effects->checkInputWindowStacking(); // keep them on top
0683     }
0684     QRectF newgeom(Xcb::fromXNative(e->x), Xcb::fromXNative(e->y), Xcb::fromXNative(e->width), Xcb::fromXNative(e->height));
0685     if (newgeom != m_frameGeometry) {
0686         Q_EMIT frameGeometryAboutToChange();
0687 
0688         QRectF old = m_frameGeometry;
0689         m_clientGeometry = newgeom;
0690         m_frameGeometry = newgeom;
0691         m_bufferGeometry = newgeom;
0692         checkOutput();
0693         Q_EMIT bufferGeometryChanged(old);
0694         Q_EMIT clientGeometryChanged(old);
0695         Q_EMIT frameGeometryChanged(old);
0696         Q_EMIT shapeChanged();
0697     }
0698 }
0699 
0700 /**
0701  * Handles configure  requests of the client window
0702  */
0703 void X11Window::configureRequestEvent(xcb_configure_request_event_t *e)
0704 {
0705     if (e->window != window()) {
0706         return; // ignore frame/wrapper
0707     }
0708     if (isInteractiveResize() || isInteractiveMove()) {
0709         return; // we have better things to do right now
0710     }
0711 
0712     if (m_fullscreenMode == FullScreenNormal) { // refuse resizing of fullscreen windows
0713         // but allow resizing fullscreen hacks in order to let them cancel fullscreen mode
0714         sendSyntheticConfigureNotify();
0715         return;
0716     }
0717     if (isSplash()) { // no manipulations with splashscreens either
0718         sendSyntheticConfigureNotify();
0719         return;
0720     }
0721 
0722     if (e->value_mask & XCB_CONFIG_WINDOW_BORDER_WIDTH) {
0723         // first, get rid of a window border
0724         m_client.setBorderWidth(0);
0725     }
0726 
0727     if (e->value_mask & (XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_WIDTH)) {
0728         configureRequest(e->value_mask, Xcb::fromXNative(e->x),
0729                          Xcb::fromXNative(e->y), Xcb::fromXNative(e->width), Xcb::fromXNative(e->height), 0, false);
0730     }
0731     if (e->value_mask & XCB_CONFIG_WINDOW_STACK_MODE) {
0732         restackWindow(e->sibling, e->stack_mode, NET::FromApplication, userTime(), false);
0733     }
0734 
0735     // Sending a synthetic configure notify always is fine, even in cases where
0736     // the ICCCM doesn't require this - it can be though of as 'the WM decided to move
0737     // the window later'. The window should not cause that many configure request,
0738     // so this should not have any significant impact. With user moving/resizing
0739     // the it should be optimized though (see also X11Window::setGeometry()/resize()/move()).
0740     sendSyntheticConfigureNotify();
0741 
0742     // SELI TODO accept configure requests for isDesktop windows (because kdesktop
0743     // may get XRANDR resize event before kwin), but check it's still at the bottom?
0744 }
0745 
0746 /**
0747  * Handles property changes of the client window
0748  */
0749 void X11Window::propertyNotifyEvent(xcb_property_notify_event_t *e)
0750 {
0751     if (e->window != window()) {
0752         return; // ignore frame/wrapper
0753     }
0754     switch (e->atom) {
0755     case XCB_ATOM_WM_NORMAL_HINTS:
0756         getWmNormalHints();
0757         break;
0758     case XCB_ATOM_WM_NAME:
0759         fetchName();
0760         break;
0761     case XCB_ATOM_WM_ICON_NAME:
0762         fetchIconicName();
0763         break;
0764     case XCB_ATOM_WM_TRANSIENT_FOR:
0765         readTransient();
0766         break;
0767     case XCB_ATOM_WM_HINTS:
0768         getIcons(); // because KWin::icon() uses WMHints as fallback
0769         break;
0770     default:
0771         if (e->atom == atoms->motif_wm_hints) {
0772             getMotifHints();
0773         } else if (e->atom == atoms->net_wm_sync_request_counter) {
0774             getSyncCounter();
0775         } else if (e->atom == atoms->activities) {
0776             checkActivities();
0777         } else if (e->atom == atoms->kde_color_sheme) {
0778             updateColorScheme();
0779         } else if (e->atom == atoms->kde_screen_edge_show) {
0780             updateShowOnScreenEdge();
0781         } else if (e->atom == atoms->kde_net_wm_appmenu_service_name) {
0782             checkApplicationMenuServiceName();
0783         } else if (e->atom == atoms->kde_net_wm_appmenu_object_path) {
0784             checkApplicationMenuObjectPath();
0785         } else if (e->atom == atoms->wm_client_leader) {
0786             getWmClientLeader();
0787         } else if (e->atom == atoms->kde_net_wm_shadow) {
0788             updateShadow();
0789         } else if (e->atom == atoms->kde_skip_close_animation) {
0790             getSkipCloseAnimation();
0791         }
0792         break;
0793     }
0794 }
0795 
0796 void X11Window::enterNotifyEvent(xcb_enter_notify_event_t *e)
0797 {
0798     if (waylandServer()) {
0799         return;
0800     }
0801     if (e->event != frameId()) {
0802         return; // care only about entering the whole frame
0803     }
0804 
0805     const bool mouseDrivenFocus = !options->focusPolicyIsReasonable() || (options->focusPolicy() == Options::FocusFollowsMouse && options->isNextFocusPrefersMouse());
0806     if (e->mode == XCB_NOTIFY_MODE_NORMAL || (e->mode == XCB_NOTIFY_MODE_UNGRAB && mouseDrivenFocus)) {
0807         pointerEnterEvent(QPoint(e->root_x, e->root_y));
0808         return;
0809     }
0810 }
0811 
0812 void X11Window::leaveNotifyEvent(xcb_leave_notify_event_t *e)
0813 {
0814     if (waylandServer()) {
0815         return;
0816     }
0817     if (e->event != frameId()) {
0818         return; // care only about leaving the whole frame
0819     }
0820     if (e->mode == XCB_NOTIFY_MODE_NORMAL) {
0821         if (!isInteractiveMoveResizePointerButtonDown()) {
0822             setInteractiveMoveResizeGravity(Gravity::None);
0823             updateCursor();
0824         }
0825         bool lostMouse = !exclusiveContains(rect(), QPointF(e->event_x, e->event_y));
0826         // 'lostMouse' wouldn't work with e.g. B2 or Keramik, which have non-rectangular decorations
0827         // (i.e. the LeaveNotify event comes before leaving the rect and no LeaveNotify event
0828         // comes after leaving the rect) - so lets check if the pointer is really outside the window
0829 
0830         // TODO this still sucks if a window appears above this one - it should lose the mouse
0831         // if this window is another window, but not if it's a popup ... maybe after KDE3.1 :(
0832         // (repeat after me 'AARGHL!')
0833         if (!lostMouse && e->detail != XCB_NOTIFY_DETAIL_INFERIOR) {
0834             Xcb::Pointer pointer(frameId());
0835             if (!pointer || !pointer->same_screen || pointer->child == XCB_WINDOW_NONE) {
0836                 // really lost the mouse
0837                 lostMouse = true;
0838             }
0839         }
0840         if (lostMouse) {
0841             pointerLeaveEvent();
0842             if (isDecorated()) {
0843                 // sending a move instead of a leave. With leave we need to send proper coords, with move it's handled internally
0844                 QHoverEvent leaveEvent(QEvent::HoverMove, QPointF(-1, -1), QPointF(-1, -1), Qt::NoModifier);
0845                 QCoreApplication::sendEvent(decoration(), &leaveEvent);
0846             }
0847         }
0848         if (options->focusPolicy() == Options::FocusStrictlyUnderMouse && isActive() && lostMouse) {
0849             workspace()->requestDelayFocus(nullptr);
0850         }
0851         return;
0852     }
0853 }
0854 
0855 static uint16_t x11CommandAllModifier()
0856 {
0857     switch (options->commandAllModifier()) {
0858     case Qt::MetaModifier:
0859         return KKeyServer::modXMeta();
0860     case Qt::AltModifier:
0861         return KKeyServer::modXAlt();
0862     default:
0863         return 0;
0864     }
0865 }
0866 
0867 #define XCapL KKeyServer::modXLock()
0868 #define XNumL KKeyServer::modXNumLock()
0869 #define XScrL KKeyServer::modXScrollLock()
0870 void X11Window::establishCommandWindowGrab(uint8_t button)
0871 {
0872     // Unfortunately there are a lot of possible modifier combinations that we need to take into
0873     // account. We tackle that problem in a kind of smart way. First, we grab the button with all
0874     // possible modifiers, then we ungrab the ones that are relevant only to commandAllx().
0875 
0876     m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, XCB_MOD_MASK_ANY, button);
0877 
0878     uint16_t x11Modifier = x11CommandAllModifier();
0879 
0880     unsigned int mods[8] = {
0881         0, XCapL, XNumL, XNumL | XCapL,
0882         XScrL, XScrL | XCapL,
0883         XScrL | XNumL, XScrL | XNumL | XCapL};
0884     for (int i = 0; i < 8; ++i) {
0885         m_wrapper.ungrabButton(x11Modifier | mods[i], button);
0886     }
0887 }
0888 
0889 void X11Window::establishCommandAllGrab(uint8_t button)
0890 {
0891     uint16_t x11Modifier = x11CommandAllModifier();
0892 
0893     unsigned int mods[8] = {
0894         0, XCapL, XNumL, XNumL | XCapL,
0895         XScrL, XScrL | XCapL,
0896         XScrL | XNumL, XScrL | XNumL | XCapL};
0897     for (int i = 0; i < 8; ++i) {
0898         m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, x11Modifier | mods[i], button);
0899     }
0900 }
0901 #undef XCapL
0902 #undef XNumL
0903 #undef XScrL
0904 
0905 void X11Window::updateMouseGrab()
0906 {
0907     if (waylandServer()) {
0908         return;
0909     }
0910 
0911     xcb_ungrab_button(kwinApp()->x11Connection(), XCB_BUTTON_INDEX_ANY, m_wrapper, XCB_MOD_MASK_ANY);
0912 
0913 #if KWIN_BUILD_TABBOX
0914     if (workspace()->tabbox()->forcedGlobalMouseGrab()) { // see TabBox::establishTabBoxGrab()
0915         m_wrapper.grabButton(XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC);
0916         return;
0917     }
0918 #endif
0919 
0920     // When a passive grab is activated or deactivated, the X server will generate crossing
0921     // events as if the pointer were suddenly to warp from its current position to some position
0922     // in the grab window. Some /broken/ X11 clients do get confused by such EnterNotify and
0923     // LeaveNotify events so we release the passive grab for the active window.
0924     //
0925     // The passive grab below is established so the window can be raised or activated when it
0926     // is clicked.
0927     if ((options->focusPolicyIsReasonable() && !isActive()) || (options->isClickRaise() && !isMostRecentlyRaised())) {
0928         if (options->commandWindow1() != Options::MouseNothing) {
0929             establishCommandWindowGrab(XCB_BUTTON_INDEX_1);
0930         }
0931         if (options->commandWindow2() != Options::MouseNothing) {
0932             establishCommandWindowGrab(XCB_BUTTON_INDEX_2);
0933         }
0934         if (options->commandWindow3() != Options::MouseNothing) {
0935             establishCommandWindowGrab(XCB_BUTTON_INDEX_3);
0936         }
0937         if (options->commandWindowWheel() != Options::MouseNothing) {
0938             establishCommandWindowGrab(XCB_BUTTON_INDEX_4);
0939             establishCommandWindowGrab(XCB_BUTTON_INDEX_5);
0940         }
0941     }
0942 
0943     // We want to grab <command modifier> + buttons no matter what state the window is in. The
0944     // window will receive funky EnterNotify and LeaveNotify events, but there is nothing that
0945     // we can do about it, unfortunately.
0946 
0947     if (!workspace()->globalShortcutsDisabled()) {
0948         if (options->commandAll1() != Options::MouseNothing) {
0949             establishCommandAllGrab(XCB_BUTTON_INDEX_1);
0950         }
0951         if (options->commandAll2() != Options::MouseNothing) {
0952             establishCommandAllGrab(XCB_BUTTON_INDEX_2);
0953         }
0954         if (options->commandAll3() != Options::MouseNothing) {
0955             establishCommandAllGrab(XCB_BUTTON_INDEX_3);
0956         }
0957         if (options->commandAllWheel() != Options::MouseWheelNothing) {
0958             establishCommandAllGrab(XCB_BUTTON_INDEX_4);
0959             establishCommandAllGrab(XCB_BUTTON_INDEX_5);
0960         }
0961     }
0962 }
0963 
0964 static bool modKeyDown(int state)
0965 {
0966     const uint keyModX = (options->keyCmdAllModKey() == Qt::Key_Meta) ? KKeyServer::modXMeta() : KKeyServer::modXAlt();
0967     return keyModX && (state & KKeyServer::accelModMaskX()) == keyModX;
0968 }
0969 
0970 // return value matters only when filtering events before decoration gets them
0971 bool X11Window::buttonPressEvent(xcb_window_t w, int button, int state, int x, int y, int x_root, int y_root, xcb_timestamp_t time)
0972 {
0973     if (waylandServer()) {
0974         return true;
0975     }
0976     if (isInteractiveMoveResizePointerButtonDown()) {
0977         if (w == wrapperId()) {
0978             xcb_allow_events(kwinApp()->x11Connection(), XCB_ALLOW_SYNC_POINTER, XCB_TIME_CURRENT_TIME); // xTime());
0979         }
0980         return true;
0981     }
0982 
0983     if (w == wrapperId() || w == frameId() || w == inputId()) {
0984         // FRAME neco s tohohle by se melo zpracovat, nez to dostane dekorace
0985         updateUserTime(time);
0986         const bool bModKeyHeld = modKeyDown(state);
0987 
0988         if (isSplash()
0989             && button == XCB_BUTTON_INDEX_1 && !bModKeyHeld) {
0990             // hide splashwindow if the user clicks on it
0991             setHidden(true);
0992             if (w == wrapperId()) {
0993                 xcb_allow_events(kwinApp()->x11Connection(), XCB_ALLOW_SYNC_POINTER, XCB_TIME_CURRENT_TIME); // xTime());
0994             }
0995             return true;
0996         }
0997 
0998         Options::MouseCommand com = Options::MouseNothing;
0999         bool was_action = false;
1000         if (bModKeyHeld) {
1001             was_action = true;
1002             switch (button) {
1003             case XCB_BUTTON_INDEX_1:
1004                 com = options->commandAll1();
1005                 break;
1006             case XCB_BUTTON_INDEX_2:
1007                 com = options->commandAll2();
1008                 break;
1009             case XCB_BUTTON_INDEX_3:
1010                 com = options->commandAll3();
1011                 break;
1012             case XCB_BUTTON_INDEX_4:
1013             case XCB_BUTTON_INDEX_5:
1014                 com = options->operationWindowMouseWheel(button == XCB_BUTTON_INDEX_4 ? 120 : -120);
1015                 break;
1016             }
1017         } else {
1018             if (w == wrapperId()) {
1019                 if (button < 4) {
1020                     com = getMouseCommand(x11ToQtMouseButton(button), &was_action);
1021                 } else if (button < 6) {
1022                     com = getWheelCommand(Qt::Vertical, &was_action);
1023                 }
1024             }
1025         }
1026         if (was_action) {
1027             bool replay = performMouseCommand(com, QPoint(x_root, y_root));
1028 
1029             if (isSpecialWindow()) {
1030                 replay = true;
1031             }
1032 
1033             if (w == wrapperId()) { // these can come only from a grab
1034                 xcb_allow_events(kwinApp()->x11Connection(), replay ? XCB_ALLOW_REPLAY_POINTER : XCB_ALLOW_SYNC_POINTER, XCB_TIME_CURRENT_TIME); // xTime());
1035             }
1036             return true;
1037         }
1038     }
1039 
1040     if (w == wrapperId()) { // these can come only from a grab
1041         xcb_allow_events(kwinApp()->x11Connection(), XCB_ALLOW_REPLAY_POINTER, XCB_TIME_CURRENT_TIME); // xTime());
1042         return true;
1043     }
1044     if (w == inputId()) {
1045         x = x_root - frameGeometry().x();
1046         y = y_root - frameGeometry().y();
1047         // New API processes core events FIRST and only passes unused ones to the decoration
1048         QMouseEvent ev(QMouseEvent::MouseButtonPress,
1049                        QPoint(x, y),
1050                        QPoint(x_root, y_root),
1051                        x11ToQtMouseButton(button),
1052                        x11ToQtMouseButtons(state) | x11ToQtMouseButton(button),
1053                        Qt::KeyboardModifiers());
1054         return processDecorationButtonPress(&ev, true);
1055     }
1056     if (w == frameId() && isDecorated()) {
1057         if (button >= 4 && button <= 7) {
1058             const Qt::KeyboardModifiers modifiers = x11ToQtKeyboardModifiers(state);
1059             // Logic borrowed from qapplication_x11.cpp
1060             const int delta = 120 * ((button == 4 || button == 6) ? 1 : -1);
1061             const bool hor = (((button == 4 || button == 5) && (modifiers & Qt::AltModifier))
1062                               || (button == 6 || button == 7));
1063 
1064             const QPoint angle = hor ? QPoint(delta, 0) : QPoint(0, delta);
1065             QWheelEvent event(QPointF(x, y),
1066                               QPointF(x_root, y_root),
1067                               QPoint(),
1068                               angle,
1069                               x11ToQtMouseButtons(state),
1070                               modifiers,
1071                               Qt::NoScrollPhase,
1072                               false);
1073             event.setAccepted(false);
1074             QCoreApplication::sendEvent(decoration(), &event);
1075             if (!event.isAccepted() && !hor) {
1076                 if (titlebarPositionUnderMouse()) {
1077                     performMouseCommand(options->operationTitlebarMouseWheel(delta), QPoint(x_root, y_root));
1078                 }
1079             }
1080         } else {
1081             QMouseEvent event(QEvent::MouseButtonPress,
1082                               QPointF(x, y),
1083                               QPointF(x_root, y_root),
1084                               x11ToQtMouseButton(button),
1085                               x11ToQtMouseButtons(state) | x11ToQtMouseButton(button),
1086                               x11ToQtKeyboardModifiers(state));
1087             event.setTimestamp(time);
1088             event.setAccepted(false);
1089             QCoreApplication::sendEvent(decoration(), &event);
1090             if (!event.isAccepted()) {
1091                 processDecorationButtonPress(&event);
1092             }
1093         }
1094         return true;
1095     }
1096     return true;
1097 }
1098 
1099 // return value matters only when filtering events before decoration gets them
1100 bool X11Window::buttonReleaseEvent(xcb_window_t w, int button, int state, int x, int y, int x_root, int y_root)
1101 {
1102     if (waylandServer()) {
1103         return true;
1104     }
1105     if (w == frameId() && isDecorated()) {
1106         // wheel handled on buttonPress
1107         if (button < 4 || button > 7) {
1108             QMouseEvent event(QEvent::MouseButtonRelease,
1109                               QPointF(x, y),
1110                               QPointF(x_root, y_root),
1111                               x11ToQtMouseButton(button),
1112                               x11ToQtMouseButtons(state) & ~x11ToQtMouseButton(button),
1113                               x11ToQtKeyboardModifiers(state));
1114             event.setAccepted(false);
1115             QCoreApplication::sendEvent(decoration(), &event);
1116             if (event.isAccepted() || !titlebarPositionUnderMouse()) {
1117                 invalidateDecorationDoubleClickTimer(); // click was for the deco and shall not init a doubleclick
1118             }
1119         }
1120     }
1121     if (w == wrapperId()) {
1122         xcb_allow_events(kwinApp()->x11Connection(), XCB_ALLOW_SYNC_POINTER, XCB_TIME_CURRENT_TIME); // xTime());
1123         return true;
1124     }
1125     if (w != frameId() && w != inputId() && w != moveResizeGrabWindow()) {
1126         return true;
1127     }
1128     if (w == frameId() && workspace()->userActionsMenu() && workspace()->userActionsMenu()->isShown()) {
1129         workspace()->userActionsMenu()->grabInput();
1130     }
1131     x = this->x(); // translate from grab window to local coords
1132     y = this->y();
1133 
1134     // Check whether other buttons are still left pressed
1135     int buttonMask = XCB_BUTTON_MASK_1 | XCB_BUTTON_MASK_2 | XCB_BUTTON_MASK_3;
1136     if (button == XCB_BUTTON_INDEX_1) {
1137         buttonMask &= ~XCB_BUTTON_MASK_1;
1138     } else if (button == XCB_BUTTON_INDEX_2) {
1139         buttonMask &= ~XCB_BUTTON_MASK_2;
1140     } else if (button == XCB_BUTTON_INDEX_3) {
1141         buttonMask &= ~XCB_BUTTON_MASK_3;
1142     }
1143 
1144     if ((state & buttonMask) == 0) {
1145         endInteractiveMoveResize();
1146     }
1147     return true;
1148 }
1149 
1150 // return value matters only when filtering events before decoration gets them
1151 bool X11Window::motionNotifyEvent(xcb_window_t w, int state, int x, int y, int x_root, int y_root)
1152 {
1153     if (waylandServer()) {
1154         return true;
1155     }
1156     if (w == frameId() && isDecorated() && !isMinimized()) {
1157         // TODO Mouse move event dependent on state
1158         QHoverEvent event(QEvent::HoverMove, QPointF(x, y), QPointF(x, y));
1159         QCoreApplication::instance()->sendEvent(decoration(), &event);
1160     }
1161     if (w != frameId() && w != inputId() && w != moveResizeGrabWindow()) {
1162         return true; // care only about the whole frame
1163     }
1164     if (!isInteractiveMoveResizePointerButtonDown()) {
1165         if (w == inputId()) {
1166             int x = x_root - frameGeometry().x(); // + padding_left;
1167             int y = y_root - frameGeometry().y(); // + padding_top;
1168 
1169             if (isDecorated()) {
1170                 QHoverEvent event(QEvent::HoverMove, QPointF(x, y), QPointF(x, y));
1171                 QCoreApplication::instance()->sendEvent(decoration(), &event);
1172             }
1173         }
1174         Gravity newGravity = modKeyDown(state) ? Gravity::None : mouseGravity();
1175         if (newGravity != interactiveMoveResizeGravity()) {
1176             setInteractiveMoveResizeGravity(newGravity);
1177             updateCursor();
1178         }
1179         return false;
1180     }
1181     if (w == moveResizeGrabWindow()) {
1182         x = this->x(); // translate from grab window to local coords
1183         y = this->y();
1184     }
1185 
1186     handleInteractiveMoveResize(QPoint(x, y), QPoint(x_root, y_root));
1187     if (isInteractiveMove()) {
1188         workspace()->screenEdges()->check(QPoint(x_root, y_root), QDateTime::fromMSecsSinceEpoch(xTime(), Qt::UTC));
1189     }
1190 
1191     return true;
1192 }
1193 
1194 void X11Window::focusInEvent(xcb_focus_in_event_t *e)
1195 {
1196     if (e->event != window()) {
1197         return; // only window gets focus
1198     }
1199     if (e->mode == XCB_NOTIFY_MODE_UNGRAB) {
1200         return; // we don't care
1201     }
1202     if (e->detail == XCB_NOTIFY_DETAIL_POINTER) {
1203         return; // we don't care
1204     }
1205     if (isShade() || !isShown() || !isOnCurrentDesktop()) { // we unmapped it, but it got focus meanwhile ->
1206         return; // activateNextWindow() already transferred focus elsewhere
1207     }
1208     workspace()->forEachClient([](X11Window *window) {
1209         window->cancelFocusOutTimer();
1210     });
1211     // check if this window is in should_get_focus list or if activation is allowed
1212     bool activate = allowWindowActivation(-1U, true);
1213     workspace()->gotFocusIn(this); // remove from should_get_focus list
1214     if (activate) {
1215         setActive(true);
1216     } else {
1217         if (workspace()->restoreFocus()) {
1218             demandAttention();
1219         } else {
1220             qCWarning(KWIN_CORE, "Failed to restore focus. Activating 0x%x", window());
1221             setActive(true);
1222         }
1223     }
1224 }
1225 
1226 void X11Window::focusOutEvent(xcb_focus_out_event_t *e)
1227 {
1228     if (e->event != window()) {
1229         return; // only window gets focus
1230     }
1231     if (e->mode == XCB_NOTIFY_MODE_GRAB) {
1232         return; // we don't care
1233     }
1234     if (isShade()) {
1235         return; // here neither
1236     }
1237     if (e->detail != XCB_NOTIFY_DETAIL_NONLINEAR
1238         && e->detail != XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL) {
1239         // SELI check all this
1240         return; // hack for motif apps like netscape
1241     }
1242     if (QApplication::activePopupWidget()) {
1243         return;
1244     }
1245 
1246     // When a window loses focus, FocusOut events are usually immediatelly
1247     // followed by FocusIn events for another window that gains the focus
1248     // (unless the focus goes to another screen, or to the nofocus widget).
1249     // Without this check, the former focused window would have to be
1250     // deactivated, and after that, the new one would be activated, with
1251     // a short time when there would be no active window. This can cause
1252     // flicker sometimes, e.g. when a fullscreen is shown, and focus is transferred
1253     // from it to its transient, the fullscreen would be kept in the Active layer
1254     // at the beginning and at the end, but not in the middle, when the active
1255     // window would be temporarily none (see X11Window::belongToLayer() ).
1256     // Therefore the setActive(false) call is moved to the end of the current
1257     // event queue. If there is a matching FocusIn event in the current queue
1258     // this will be processed before the setActive(false) call and the activation
1259     // of the window which gained FocusIn will automatically deactivate the
1260     // previously active window.
1261     if (!m_focusOutTimer) {
1262         m_focusOutTimer = new QTimer(this);
1263         m_focusOutTimer->setSingleShot(true);
1264         m_focusOutTimer->setInterval(0);
1265         connect(m_focusOutTimer, &QTimer::timeout, this, [this]() {
1266             setActive(false);
1267         });
1268     }
1269     m_focusOutTimer->start();
1270 }
1271 
1272 // performs _NET_WM_MOVERESIZE
1273 void X11Window::NETMoveResize(qreal x_root, qreal y_root, NET::Direction direction)
1274 {
1275     if (direction == NET::Move) {
1276         // move cursor to the provided position to prevent the window jumping there on first movement
1277         // the expectation is that the cursor is already at the provided position,
1278         // thus it's more a safety measurement
1279         Cursors::self()->mouse()->setPos(QPointF(x_root, y_root));
1280         performMouseCommand(Options::MouseMove, QPointF(x_root, y_root));
1281     } else if (isInteractiveMoveResize() && direction == NET::MoveResizeCancel) {
1282         finishInteractiveMoveResize(true);
1283         setInteractiveMoveResizePointerButtonDown(false);
1284         updateCursor();
1285     } else if (direction >= NET::TopLeft && direction <= NET::Left) {
1286         static const Gravity convert[] = {
1287             Gravity::TopLeft,
1288             Gravity::Top,
1289             Gravity::TopRight,
1290             Gravity::Right,
1291             Gravity::BottomRight,
1292             Gravity::Bottom,
1293             Gravity::BottomLeft,
1294             Gravity::Left};
1295         if (!isResizable() || isShade()) {
1296             return;
1297         }
1298         if (isInteractiveMoveResize()) {
1299             finishInteractiveMoveResize(false);
1300         }
1301         setInteractiveMoveResizePointerButtonDown(true);
1302         setInteractiveMoveOffset(QPointF(x_root - x(), y_root - y())); // map from global
1303         setInvertedInteractiveMoveOffset(rect().bottomRight() - interactiveMoveOffset());
1304         setUnrestrictedInteractiveMoveResize(false);
1305         setInteractiveMoveResizeGravity(convert[direction]);
1306         if (!startInteractiveMoveResize()) {
1307             setInteractiveMoveResizePointerButtonDown(false);
1308         }
1309         updateCursor();
1310     } else if (direction == NET::KeyboardMove) {
1311         // ignore mouse coordinates given in the message, mouse position is used by the moving algorithm
1312         Cursors::self()->mouse()->setPos(frameGeometry().center());
1313         performMouseCommand(Options::MouseUnrestrictedMove, frameGeometry().center());
1314     } else if (direction == NET::KeyboardSize) {
1315         // ignore mouse coordinates given in the message, mouse position is used by the resizing algorithm
1316         Cursors::self()->mouse()->setPos(frameGeometry().bottomRight());
1317         performMouseCommand(Options::MouseUnrestrictedResize, frameGeometry().bottomRight());
1318     }
1319 }
1320 
1321 void X11Window::keyPressEvent(uint key_code, xcb_timestamp_t time)
1322 {
1323     updateUserTime(time);
1324     Window::keyPressEvent(key_code);
1325 }
1326 
1327 } // namespace