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