File indexing completed on 2024-04-28 16:48:46

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