KWin
Loading...
Searching...
No Matches
x11_standalone_windowselector.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
7 SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10*/
12#include "cursor.h"
13#include "utils/xcbutils.h"
14#include "workspace.h"
15#include "x11window.h"
16// XLib
17#include <X11/Xutil.h>
18#include <X11/cursorfont.h>
19#include <fixx11h.h>
20// XCB
21#include <xcb/xcb_keysyms.h>
22
23namespace KWin
24{
25
27 : X11EventFilter(QList<int>{
28 XCB_BUTTON_PRESS,
29 XCB_BUTTON_RELEASE,
30 XCB_MOTION_NOTIFY,
31 XCB_ENTER_NOTIFY,
32 XCB_LEAVE_NOTIFY,
33 XCB_KEY_PRESS,
34 XCB_KEY_RELEASE,
35 XCB_FOCUS_IN,
36 XCB_FOCUS_OUT,
37 })
38 , m_active(false)
39{
40}
41
45
46void WindowSelector::start(std::function<void(KWin::Window *)> callback, const QByteArray &cursorName)
47{
48 if (m_active) {
49 callback(nullptr);
50 return;
51 }
52
53 m_active = activate(cursorName);
54 if (!m_active) {
55 callback(nullptr);
56 return;
57 }
58 m_callback = callback;
59}
60
61void WindowSelector::start(std::function<void(const QPointF &)> callback)
62{
63 if (m_active) {
64 callback(QPoint(-1, -1));
65 return;
66 }
67
68 m_active = activate();
69 if (!m_active) {
70 callback(QPoint(-1, -1));
71 return;
72 }
73 m_pointSelectionFallback = callback;
74}
75
76bool WindowSelector::activate(const QByteArray &cursorName)
77{
78 xcb_cursor_t cursor = createCursor(cursorName);
79
80 xcb_connection_t *c = connection();
81 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));
82 if (!grabPointer || grabPointer->status != XCB_GRAB_STATUS_SUCCESS) {
83 return false;
84 }
85 const bool grabbed = grabXKeyboard();
86 if (grabbed) {
88 } else {
89 xcb_ungrab_pointer(connection(), XCB_TIME_CURRENT_TIME);
90 }
91 return grabbed;
92}
93
94xcb_cursor_t WindowSelector::createCursor(const QByteArray &cursorName)
95{
96 if (cursorName.isEmpty()) {
97 return Cursors::self()->mouse()->x11Cursor(Qt::CrossCursor);
98 }
99 xcb_cursor_t cursor = Cursors::self()->mouse()->x11Cursor(cursorName);
100 if (cursor != XCB_CURSOR_NONE) {
101 return cursor;
102 }
103 if (cursorName == QByteArrayLiteral("pirate")) {
104 // special handling for font pirate cursor
105 static xcb_cursor_t kill_cursor = XCB_CURSOR_NONE;
106 if (kill_cursor != XCB_CURSOR_NONE) {
107 return kill_cursor;
108 }
109 // fallback on font
110 xcb_connection_t *c = connection();
111 const xcb_font_t cursorFont = xcb_generate_id(c);
112 xcb_open_font(c, cursorFont, strlen("cursor"), "cursor");
113 cursor = xcb_generate_id(c);
114 xcb_create_glyph_cursor(c, cursor, cursorFont, cursorFont,
115 XC_pirate, /* source character glyph */
116 XC_pirate + 1, /* mask character glyph */
117 0, 0, 0, 0, 0, 0); /* r b g r b g */
118 kill_cursor = cursor;
119 }
120 return cursor;
121}
122
123void WindowSelector::processEvent(xcb_generic_event_t *event)
124{
125 if (event->response_type == XCB_BUTTON_RELEASE) {
126 xcb_button_release_event_t *buttonEvent = reinterpret_cast<xcb_button_release_event_t *>(event);
127 handleButtonRelease(buttonEvent->detail, buttonEvent->child);
128 } else if (event->response_type == XCB_KEY_PRESS) {
129 xcb_key_press_event_t *keyEvent = reinterpret_cast<xcb_key_press_event_t *>(event);
130 handleKeyPress(keyEvent->detail, keyEvent->state);
131 }
132}
133
134bool WindowSelector::event(xcb_generic_event_t *event)
135{
136 if (!m_active) {
137 return false;
138 }
140
141 return true;
142}
143
144void WindowSelector::handleButtonRelease(xcb_button_t button, xcb_window_t window)
145{
146 if (button == XCB_BUTTON_INDEX_3) {
147 cancelCallback();
148 release();
149 return;
150 }
151 if (button == XCB_BUTTON_INDEX_1 || button == XCB_BUTTON_INDEX_2) {
152 if (m_callback) {
153 selectWindowId(window);
154 } else if (m_pointSelectionFallback) {
155 m_pointSelectionFallback(Cursors::self()->mouse()->pos());
156 }
157 release();
158 return;
159 }
160}
161
162void WindowSelector::handleKeyPress(xcb_keycode_t keycode, uint16_t state)
163{
164 xcb_key_symbols_t *symbols = xcb_key_symbols_alloc(connection());
165 xcb_keysym_t kc = xcb_key_symbols_get_keysym(symbols, keycode, 0);
166 int mx = 0;
167 int my = 0;
168 const bool returnPressed = (kc == XK_Return) || (kc == XK_space);
169 const bool escapePressed = (kc == XK_Escape);
170 if (kc == XK_Left) {
171 mx = -10;
172 }
173 if (kc == XK_Right) {
174 mx = 10;
175 }
176 if (kc == XK_Up) {
177 my = -10;
178 }
179 if (kc == XK_Down) {
180 my = 10;
181 }
182 if (state & XCB_MOD_MASK_CONTROL) {
183 mx /= 10;
184 my /= 10;
185 }
186 Cursors::self()->mouse()->setPos(Cursors::self()->mouse()->pos() + QPoint(mx, my));
187 if (returnPressed) {
188 if (m_callback) {
189 selectWindowUnderPointer();
190 } else if (m_pointSelectionFallback) {
191 m_pointSelectionFallback(Cursors::self()->mouse()->pos());
192 }
193 }
194 if (returnPressed || escapePressed) {
195 if (escapePressed) {
196 cancelCallback();
197 }
198 release();
199 }
200 xcb_key_symbols_free(symbols);
201}
202
203void WindowSelector::selectWindowUnderPointer()
204{
205 Xcb::Pointer pointer(rootWindow());
206 if (!pointer.isNull() && pointer->child != XCB_WINDOW_NONE) {
207 selectWindowId(pointer->child);
208 }
209}
210
211void WindowSelector::release()
212{
214 xcb_ungrab_pointer(connection(), XCB_TIME_CURRENT_TIME);
216 m_active = false;
217 m_callback = std::function<void(KWin::Window *)>();
218 m_pointSelectionFallback = std::function<void(const QPointF &)>();
219}
220
221void WindowSelector::selectWindowId(xcb_window_t window_to_select)
222{
223 if (window_to_select == XCB_WINDOW_NONE) {
224 m_callback(nullptr);
225 return;
226 }
227 xcb_window_t window = window_to_select;
228 X11Window *client = nullptr;
229 while (true) {
231 if (client) {
232 break; // Found the client
233 }
234 Xcb::Tree tree(window);
235 if (window == tree->root) {
236 // We didn't find the client, probably an override-redirect window
237 break;
238 }
239 window = tree->parent; // Go up
240 }
241 if (client) {
242 m_callback(client);
243 } else {
244 m_callback(Workspace::self()->findUnmanaged(window_to_select));
245 }
246}
247
248void WindowSelector::cancelCallback()
249{
250 if (m_callback) {
251 m_callback(nullptr);
252 } else if (m_pointSelectionFallback) {
253 m_pointSelectionFallback(QPoint(-1, -1));
254 }
255}
256
257} // namespace
xcb_cursor_t x11Cursor(CursorShape shape)
Definition cursor.cpp:225
void setPos(const QPointF &pos)
Definition cursor.cpp:210
static Cursors * self()
Definition cursor.cpp:35
Cursor * mouse() const
Definition cursor.h:266
void start(std::function< void(KWin::Window *)> callback, const QByteArray &cursorName)
void processEvent(xcb_generic_event_t *event)
bool event(xcb_generic_event_t *event) override
X11Window * findClient(std::function< bool(const X11Window *)> func) const
Finds the first Client matching the condition expressed by passed in func.
static Workspace * self()
Definition workspace.h:91
KWIN_EXPORT xcb_window_t rootWindow()
Definition xcb.h:24
KWIN_EXPORT xcb_connection_t * connection()
Definition xcb.h:19
void KWIN_EXPORT ungrabXKeyboard()
Definition common.cpp:121
void KWIN_EXPORT grabXServer()
Definition common.cpp:72
void KWIN_EXPORT ungrabXServer()
Definition common.cpp:79
bool KWIN_EXPORT grabXKeyboard(xcb_window_t w=XCB_WINDOW_NONE)
Definition common.cpp:90
std::unique_ptr< T, CDeleter > UniqueCPtr
Definition c_ptr.h:28
struct _XCBKeySymbols xcb_key_symbols_t