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