File indexing completed on 2025-04-27 11:33:01
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 SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 #include "x11_standalone_windowselector.h" 0012 #include "cursor.h" 0013 #include "unmanaged.h" 0014 #include "utils/xcbutils.h" 0015 #include "workspace.h" 0016 #include "x11window.h" 0017 // XLib 0018 #include <X11/Xutil.h> 0019 #include <X11/cursorfont.h> 0020 #include <fixx11h.h> 0021 // XCB 0022 #include <xcb/xcb_keysyms.h> 0023 0024 namespace KWin 0025 { 0026 0027 WindowSelector::WindowSelector() 0028 : X11EventFilter(QVector<int>{ 0029 XCB_BUTTON_PRESS, 0030 XCB_BUTTON_RELEASE, 0031 XCB_MOTION_NOTIFY, 0032 XCB_ENTER_NOTIFY, 0033 XCB_LEAVE_NOTIFY, 0034 XCB_KEY_PRESS, 0035 XCB_KEY_RELEASE, 0036 XCB_FOCUS_IN, 0037 XCB_FOCUS_OUT, 0038 }) 0039 , m_active(false) 0040 { 0041 } 0042 0043 WindowSelector::~WindowSelector() 0044 { 0045 } 0046 0047 void WindowSelector::start(std::function<void(KWin::Window *)> callback, const QByteArray &cursorName) 0048 { 0049 if (m_active) { 0050 callback(nullptr); 0051 return; 0052 } 0053 0054 m_active = activate(cursorName); 0055 if (!m_active) { 0056 callback(nullptr); 0057 return; 0058 } 0059 m_callback = callback; 0060 } 0061 0062 void WindowSelector::start(std::function<void(const QPoint &)> callback) 0063 { 0064 if (m_active) { 0065 callback(QPoint(-1, -1)); 0066 return; 0067 } 0068 0069 m_active = activate(); 0070 if (!m_active) { 0071 callback(QPoint(-1, -1)); 0072 return; 0073 } 0074 m_pointSelectionFallback = callback; 0075 } 0076 0077 bool WindowSelector::activate(const QByteArray &cursorName) 0078 { 0079 xcb_cursor_t cursor = createCursor(cursorName); 0080 0081 xcb_connection_t *c = connection(); 0082 UniqueCPtr<xcb_grab_pointer_reply_t> grabPointer(xcb_grab_pointer_reply(c, xcb_grab_pointer_unchecked(c, false, rootWindow(), XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC, XCB_WINDOW_NONE, cursor, XCB_TIME_CURRENT_TIME), nullptr)); 0083 if (!grabPointer || grabPointer->status != XCB_GRAB_STATUS_SUCCESS) { 0084 return false; 0085 } 0086 const bool grabbed = grabXKeyboard(); 0087 if (grabbed) { 0088 grabXServer(); 0089 } else { 0090 xcb_ungrab_pointer(connection(), XCB_TIME_CURRENT_TIME); 0091 } 0092 return grabbed; 0093 } 0094 0095 xcb_cursor_t WindowSelector::createCursor(const QByteArray &cursorName) 0096 { 0097 if (cursorName.isEmpty()) { 0098 return Cursors::self()->mouse()->x11Cursor(Qt::CrossCursor); 0099 } 0100 xcb_cursor_t cursor = Cursors::self()->mouse()->x11Cursor(cursorName); 0101 if (cursor != XCB_CURSOR_NONE) { 0102 return cursor; 0103 } 0104 if (cursorName == QByteArrayLiteral("pirate")) { 0105 // special handling for font pirate cursor 0106 static xcb_cursor_t kill_cursor = XCB_CURSOR_NONE; 0107 if (kill_cursor != XCB_CURSOR_NONE) { 0108 return kill_cursor; 0109 } 0110 // fallback on font 0111 xcb_connection_t *c = connection(); 0112 const xcb_font_t cursorFont = xcb_generate_id(c); 0113 xcb_open_font(c, cursorFont, strlen("cursor"), "cursor"); 0114 cursor = xcb_generate_id(c); 0115 xcb_create_glyph_cursor(c, cursor, cursorFont, cursorFont, 0116 XC_pirate, /* source character glyph */ 0117 XC_pirate + 1, /* mask character glyph */ 0118 0, 0, 0, 0, 0, 0); /* r b g r b g */ 0119 kill_cursor = cursor; 0120 } 0121 return cursor; 0122 } 0123 0124 void WindowSelector::processEvent(xcb_generic_event_t *event) 0125 { 0126 if (event->response_type == XCB_BUTTON_RELEASE) { 0127 xcb_button_release_event_t *buttonEvent = reinterpret_cast<xcb_button_release_event_t *>(event); 0128 handleButtonRelease(buttonEvent->detail, buttonEvent->child); 0129 } else if (event->response_type == XCB_KEY_PRESS) { 0130 xcb_key_press_event_t *keyEvent = reinterpret_cast<xcb_key_press_event_t *>(event); 0131 handleKeyPress(keyEvent->detail, keyEvent->state); 0132 } 0133 } 0134 0135 bool WindowSelector::event(xcb_generic_event_t *event) 0136 { 0137 if (!m_active) { 0138 return false; 0139 } 0140 processEvent(event); 0141 0142 return true; 0143 } 0144 0145 void WindowSelector::handleButtonRelease(xcb_button_t button, xcb_window_t window) 0146 { 0147 if (button == XCB_BUTTON_INDEX_3) { 0148 cancelCallback(); 0149 release(); 0150 return; 0151 } 0152 if (button == XCB_BUTTON_INDEX_1 || button == XCB_BUTTON_INDEX_2) { 0153 if (m_callback) { 0154 selectWindowId(window); 0155 } else if (m_pointSelectionFallback) { 0156 m_pointSelectionFallback(Cursors::self()->mouse()->pos()); 0157 } 0158 release(); 0159 return; 0160 } 0161 } 0162 0163 void WindowSelector::handleKeyPress(xcb_keycode_t keycode, uint16_t state) 0164 { 0165 xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(connection()); 0166 xcb_keysym_t kc = xcb_key_symbols_get_keysym(symbols, keycode, 0); 0167 int mx = 0; 0168 int my = 0; 0169 const bool returnPressed = (kc == XK_Return) || (kc == XK_space); 0170 const bool escapePressed = (kc == XK_Escape); 0171 if (kc == XK_Left) { 0172 mx = -10; 0173 } 0174 if (kc == XK_Right) { 0175 mx = 10; 0176 } 0177 if (kc == XK_Up) { 0178 my = -10; 0179 } 0180 if (kc == XK_Down) { 0181 my = 10; 0182 } 0183 if (state & XCB_MOD_MASK_CONTROL) { 0184 mx /= 10; 0185 my /= 10; 0186 } 0187 Cursors::self()->mouse()->setPos(Cursors::self()->mouse()->pos() + QPoint(mx, my)); 0188 if (returnPressed) { 0189 if (m_callback) { 0190 selectWindowUnderPointer(); 0191 } else if (m_pointSelectionFallback) { 0192 m_pointSelectionFallback(Cursors::self()->mouse()->pos()); 0193 } 0194 } 0195 if (returnPressed || escapePressed) { 0196 if (escapePressed) { 0197 cancelCallback(); 0198 } 0199 release(); 0200 } 0201 xcb_key_symbols_free(symbols); 0202 } 0203 0204 void WindowSelector::selectWindowUnderPointer() 0205 { 0206 Xcb::Pointer pointer(rootWindow()); 0207 if (!pointer.isNull() && pointer->child != XCB_WINDOW_NONE) { 0208 selectWindowId(pointer->child); 0209 } 0210 } 0211 0212 void WindowSelector::release() 0213 { 0214 ungrabXKeyboard(); 0215 xcb_ungrab_pointer(connection(), XCB_TIME_CURRENT_TIME); 0216 ungrabXServer(); 0217 m_active = false; 0218 m_callback = std::function<void(KWin::Window *)>(); 0219 m_pointSelectionFallback = std::function<void(const QPoint &)>(); 0220 } 0221 0222 void WindowSelector::selectWindowId(xcb_window_t window_to_select) 0223 { 0224 if (window_to_select == XCB_WINDOW_NONE) { 0225 m_callback(nullptr); 0226 return; 0227 } 0228 xcb_window_t window = window_to_select; 0229 X11Window *client = nullptr; 0230 while (true) { 0231 client = Workspace::self()->findClient(Predicate::FrameIdMatch, window); 0232 if (client) { 0233 break; // Found the client 0234 } 0235 Xcb::Tree tree(window); 0236 if (window == tree->root) { 0237 // We didn't find the client, probably an override-redirect window 0238 break; 0239 } 0240 window = tree->parent; // Go up 0241 } 0242 if (client) { 0243 m_callback(client); 0244 } else { 0245 m_callback(Workspace::self()->findUnmanaged(window_to_select)); 0246 } 0247 } 0248 0249 void WindowSelector::cancelCallback() 0250 { 0251 if (m_callback) { 0252 m_callback(nullptr); 0253 } else if (m_pointSelectionFallback) { 0254 m_pointSelectionFallback(QPoint(-1, -1)); 0255 } 0256 } 0257 0258 } // namespace