Warning, file /plasma/kwin/src/events.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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