1 | // Copyright (C) 2016 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qwaylanddisplay_p.h" |
5 | |
6 | #include "qwaylandintegration_p.h" |
7 | #include "qwaylandwindow_p.h" |
8 | #include "qwaylandsurface_p.h" |
9 | #include "qwaylandabstractdecoration_p.h" |
10 | #include "qwaylandscreen_p.h" |
11 | #include "qwaylandcursor_p.h" |
12 | #include "qwaylandinputdevice_p.h" |
13 | #if QT_CONFIG(clipboard) |
14 | #include "qwaylandclipboard_p.h" |
15 | #endif |
16 | #if QT_CONFIG(wayland_datadevice) |
17 | #include "qwaylanddatadevicemanager_p.h" |
18 | #include "qwaylanddatadevice_p.h" |
19 | #endif // QT_CONFIG(wayland_datadevice) |
20 | #if QT_CONFIG(wayland_client_primary_selection) |
21 | #include "qwaylandprimaryselectionv1_p.h" |
22 | #endif // QT_CONFIG(wayland_client_primary_selection) |
23 | #if QT_CONFIG(cursor) |
24 | #include <wayland-cursor.h> |
25 | #endif |
26 | #include "qwaylandhardwareintegration_p.h" |
27 | #include "qwaylandtextinputv1_p.h" |
28 | #include "qwaylandtextinputv2_p.h" |
29 | #include "qwaylandtextinputv3_p.h" |
30 | #include "qwaylandinputcontext_p.h" |
31 | #include "qwaylandinputmethodcontext_p.h" |
32 | |
33 | #include "qwaylandwindowmanagerintegration_p.h" |
34 | #include "qwaylandshellintegration_p.h" |
35 | #include "qwaylandclientbufferintegration_p.h" |
36 | |
37 | #include "qwaylandextendedsurface_p.h" |
38 | #include "qwaylandpointergestures_p.h" |
39 | #include "qwaylandsubsurface_p.h" |
40 | #include "qwaylandtouch_p.h" |
41 | #if QT_CONFIG(tabletevent) |
42 | #include "qwaylandtabletv2_p.h" |
43 | #endif |
44 | #include "qwaylandqtkey_p.h" |
45 | |
46 | #include <QtWaylandClient/private/qwayland-text-input-unstable-v1.h> |
47 | #include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> |
48 | #include <QtWaylandClient/private/qwayland-text-input-unstable-v3.h> |
49 | #include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h> |
50 | #include <QtWaylandClient/private/qwayland-qt-text-input-method-unstable-v1.h> |
51 | #include <QtWaylandClient/private/qwayland-fractional-scale-v1.h> |
52 | #include <QtWaylandClient/private/qwayland-viewporter.h> |
53 | #include <QtWaylandClient/private/qwayland-cursor-shape-v1.h> |
54 | #include <QtWaylandClient/private/qwayland-xdg-toplevel-drag-v1.h> |
55 | |
56 | #include <QtCore/private/qcore_unix_p.h> |
57 | |
58 | #include <QtCore/QAbstractEventDispatcher> |
59 | #include <QtGui/qpa/qwindowsysteminterface.h> |
60 | #include <QtGui/private/qguiapplication_p.h> |
61 | |
62 | #include <QtCore/QDebug> |
63 | |
64 | #include <errno.h> |
65 | |
66 | #include <tuple> // for std::tie |
67 | |
68 | QT_BEGIN_NAMESPACE |
69 | |
70 | namespace QtWaylandClient { |
71 | |
72 | class EventThread : public QThread |
73 | { |
74 | Q_OBJECT |
75 | public: |
76 | enum OperatingMode { |
77 | EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread. |
78 | SelfDispatch, // Dispatch the events inside this thread. |
79 | }; |
80 | |
81 | EventThread(struct wl_display * wl, struct wl_event_queue * ev_queue, |
82 | OperatingMode mode) |
83 | : m_fd(wl_display_get_fd(wl)) |
84 | , m_pipefd{ -1, -1 } |
85 | , m_wldisplay(wl) |
86 | , m_wlevqueue(ev_queue) |
87 | , m_mode(mode) |
88 | , m_reading(true) |
89 | , m_quitting(false) |
90 | { |
91 | setObjectName(QStringLiteral("WaylandEventThread")); |
92 | } |
93 | |
94 | void readAndDispatchEvents() |
95 | { |
96 | /* |
97 | * Dispatch pending events and flush the requests at least once. If the event thread |
98 | * is not reading, try to call _prepare_read() to allow the event thread to poll(). |
99 | * If that fails, re-try dispatch & flush again until _prepare_read() is successful. |
100 | * |
101 | * This allow any call to readAndDispatchEvents() to start event thread's polling, |
102 | * not only the one issued from event thread's waitForReading(), which means functions |
103 | * called from dispatch_pending() can safely spin an event loop. |
104 | */ |
105 | if (m_quitting) |
106 | return; |
107 | |
108 | for (;;) { |
109 | if (dispatchQueuePending() < 0) { |
110 | Q_EMIT waylandError(); |
111 | m_quitting = true; |
112 | return; |
113 | } |
114 | |
115 | wl_display_flush(m_wldisplay); |
116 | |
117 | // We have to check if event thread is reading every time we dispatch |
118 | // something, as that may recursively call this function. |
119 | if (m_reading.loadAcquire()) |
120 | break; |
121 | |
122 | if (prepareReadQueue() == 0) { |
123 | QMutexLocker l(&m_mutex); |
124 | m_reading.storeRelease(newValue: true); |
125 | m_cond.wakeOne(); |
126 | break; |
127 | } |
128 | } |
129 | } |
130 | |
131 | void stop() |
132 | { |
133 | // We have to both write to the pipe and set the flag, as the thread may be |
134 | // either in the poll() or waiting for _prepare_read(). |
135 | if (m_pipefd[1] != -1 && write(fd: m_pipefd[1], buf: "\0", n: 1) == -1) |
136 | qWarning(msg: "Failed to write to the pipe: %s.", strerror(errno)); |
137 | |
138 | { |
139 | QMutexLocker l(&m_mutex); |
140 | m_quitting = true; |
141 | m_cond.wakeOne(); |
142 | } |
143 | |
144 | wait(); |
145 | } |
146 | |
147 | Q_SIGNALS: |
148 | void needReadAndDispatch(); |
149 | void waylandError(); |
150 | |
151 | protected: |
152 | void run() override |
153 | { |
154 | // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets |
155 | // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore. |
156 | struct Pipe |
157 | { |
158 | Pipe(int *fds) |
159 | : fds(fds) |
160 | { |
161 | if (qt_safe_pipe(pipefd: fds) != 0) |
162 | qWarning(msg: "Pipe creation failed. Quitting may hang."); |
163 | } |
164 | ~Pipe() |
165 | { |
166 | if (fds[0] != -1) { |
167 | close(fd: fds[0]); |
168 | close(fd: fds[1]); |
169 | } |
170 | } |
171 | |
172 | int *fds; |
173 | } pipe(m_pipefd); |
174 | |
175 | // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the |
176 | // outbound ones. Wait until it's done before proceeding, unless we're told to quit. |
177 | while (waitForReading()) { |
178 | if (!m_reading.loadRelaxed()) |
179 | break; |
180 | |
181 | pollfd fds[2] = { { .fd: m_fd, POLLIN, .revents: 0 }, { .fd: m_pipefd[0], POLLIN, .revents: 0 } }; |
182 | poll(fds: fds, nfds: 2, timeout: -1); |
183 | |
184 | if (fds[1].revents & POLLIN) { |
185 | // we don't really care to read the byte that was written here since we're closing down |
186 | wl_display_cancel_read(m_wldisplay); |
187 | break; |
188 | } |
189 | |
190 | if (fds[0].revents & POLLIN) |
191 | wl_display_read_events(m_wldisplay); |
192 | // The polll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop |
193 | // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which |
194 | // case we don't care anymore about them. |
195 | else |
196 | wl_display_cancel_read(m_wldisplay); |
197 | } |
198 | } |
199 | |
200 | private: |
201 | bool waitForReading() |
202 | { |
203 | Q_ASSERT(QThread::currentThread() == this); |
204 | |
205 | m_reading.storeRelease(newValue: false); |
206 | |
207 | if (m_mode == SelfDispatch) { |
208 | readAndDispatchEvents(); |
209 | } else { |
210 | Q_EMIT needReadAndDispatch(); |
211 | |
212 | QMutexLocker lock(&m_mutex); |
213 | // m_reading might be set from our emit or some other invocation of |
214 | // readAndDispatchEvents(). |
215 | while (!m_reading.loadRelaxed() && !m_quitting) |
216 | m_cond.wait(lockedMutex: &m_mutex); |
217 | } |
218 | |
219 | return !m_quitting; |
220 | } |
221 | |
222 | int dispatchQueuePending() |
223 | { |
224 | if (m_wlevqueue) |
225 | return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue); |
226 | else |
227 | return wl_display_dispatch_pending(m_wldisplay); |
228 | } |
229 | |
230 | int prepareReadQueue() |
231 | { |
232 | if (m_wlevqueue) |
233 | return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue); |
234 | else |
235 | return wl_display_prepare_read(m_wldisplay); |
236 | } |
237 | |
238 | int m_fd; |
239 | int m_pipefd[2]; |
240 | wl_display *m_wldisplay; |
241 | wl_event_queue *m_wlevqueue; |
242 | OperatingMode m_mode; |
243 | |
244 | /* Concurrency note when operating in EmitToDispatch mode: |
245 | * m_reading is set to false inside event thread's waitForReading(), and is |
246 | * set to true inside main thread's readAndDispatchEvents(). |
247 | * The lock is not taken when setting m_reading to false, as the main thread |
248 | * is not actively waiting for it to turn false. However, the lock is taken |
249 | * inside readAndDispatchEvents() before setting m_reading to true, |
250 | * as the event thread is actively waiting for it under the wait condition. |
251 | */ |
252 | |
253 | QAtomicInteger<bool> m_reading; |
254 | bool m_quitting; |
255 | QMutex m_mutex; |
256 | QWaitCondition m_cond; |
257 | }; |
258 | |
259 | Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging |
260 | |
261 | struct wl_surface *QWaylandDisplay::createSurface(void *handle) |
262 | { |
263 | struct wl_surface *surface = mGlobals.compositor->create_surface(); |
264 | wl_surface_set_user_data(surface, handle); |
265 | return surface; |
266 | } |
267 | |
268 | struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion) |
269 | { |
270 | struct ::wl_region *region = mGlobals.compositor->create_region(); |
271 | |
272 | for (const QRect &rect : qregion) |
273 | wl_region_add(region, rect.x(), rect.y(), rect.width(), rect.height()); |
274 | |
275 | return region; |
276 | } |
277 | |
278 | ::wl_subsurface *QWaylandDisplay::createSubSurface(QWaylandWindow *window, QWaylandWindow *parent) |
279 | { |
280 | if (!mGlobals.subCompositor) { |
281 | qCWarning(lcQpaWayland) << "Can't create subsurface, not supported by the compositor."; |
282 | return nullptr; |
283 | } |
284 | |
285 | // Make sure we don't pass NULL surfaces to libwayland (crashes) |
286 | Q_ASSERT(parent->wlSurface()); |
287 | Q_ASSERT(window->wlSurface()); |
288 | |
289 | return mGlobals.subCompositor->get_subsurface(window->wlSurface(), parent->wlSurface()); |
290 | } |
291 | |
292 | ::wp_viewport *QWaylandDisplay::createViewport(QWaylandWindow *window) |
293 | { |
294 | if (!mGlobals.viewporter) { |
295 | qCWarning(lcQpaWayland) << "Can't create wp_viewport, not supported by the compositor."; |
296 | return nullptr; |
297 | } |
298 | |
299 | Q_ASSERT(window->wlSurface()); |
300 | return mGlobals.viewporter->get_viewport(window->wlSurface()); |
301 | } |
302 | |
303 | QWaylandShellIntegration *QWaylandDisplay::shellIntegration() const |
304 | { |
305 | return mWaylandIntegration->shellIntegration(); |
306 | } |
307 | |
308 | QWaylandClientBufferIntegration * QWaylandDisplay::clientBufferIntegration() const |
309 | { |
310 | return mWaylandIntegration->clientBufferIntegration(); |
311 | } |
312 | |
313 | QWaylandWindowManagerIntegration *QWaylandDisplay::windowManagerIntegration() const |
314 | { |
315 | return mGlobals.windowManagerIntegration.get(); |
316 | } |
317 | |
318 | QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration) |
319 | : mWaylandIntegration(waylandIntegration) |
320 | { |
321 | qRegisterMetaType<uint32_t>(typeName: "uint32_t"); |
322 | |
323 | mDisplay = wl_display_connect(nullptr); |
324 | if (mDisplay) { |
325 | setupConnection(); |
326 | } else { |
327 | qErrnoWarning(errno, msg: "Failed to create wl_display"); |
328 | } |
329 | |
330 | mWaylandTryReconnect = qEnvironmentVariableIsSet(varName: "QT_WAYLAND_RECONNECT"); |
331 | } |
332 | |
333 | void QWaylandDisplay::setupConnection() |
334 | { |
335 | struct ::wl_registry *registry = wl_display_get_registry(mDisplay); |
336 | init(registry); |
337 | |
338 | #if QT_CONFIG(xkbcommon) |
339 | mXkbContext.reset(p: xkb_context_new(flags: XKB_CONTEXT_NO_FLAGS)); |
340 | if (!mXkbContext) |
341 | qCWarning(lcQpaWayland, "failed to create xkb context"); |
342 | #endif |
343 | if (mWaylandInputContextRequested) |
344 | checkTextInputProtocol(); |
345 | } |
346 | |
347 | QWaylandDisplay::~QWaylandDisplay(void) |
348 | { |
349 | if (m_eventThread) |
350 | m_eventThread->stop(); |
351 | |
352 | if (m_frameEventQueueThread) |
353 | m_frameEventQueueThread->stop(); |
354 | |
355 | if (mSyncCallback) |
356 | wl_callback_destroy(mSyncCallback); |
357 | |
358 | qDeleteAll(c: std::exchange(obj&: mInputDevices, new_val: {})); |
359 | |
360 | for (QWaylandScreen *screen : std::exchange(obj&: mScreens, new_val: {})) { |
361 | QWindowSystemInterface::handleScreenRemoved(screen); |
362 | } |
363 | qDeleteAll(c: mWaitingScreens); |
364 | |
365 | #if QT_CONFIG(cursor) |
366 | mCursorThemes.clear(); |
367 | #endif |
368 | |
369 | if (m_frameEventQueue) |
370 | wl_event_queue_destroy(m_frameEventQueue); |
371 | |
372 | // Reset the globals manually since they need to be destroyed before the wl_display |
373 | mGlobals = {}; |
374 | |
375 | if (object()) |
376 | wl_registry_destroy(object()); |
377 | |
378 | if (mDisplay) |
379 | wl_display_disconnect(mDisplay); |
380 | } |
381 | |
382 | // Steps which is called just after constructor. This separates registry_global() out of the constructor |
383 | // so that factory functions in integration can be overridden. |
384 | bool QWaylandDisplay::initialize() |
385 | { |
386 | if (!isInitialized()) |
387 | return false; |
388 | |
389 | forceRoundTrip(); |
390 | |
391 | emit connected(); |
392 | |
393 | if (!mWaitingScreens.isEmpty()) { |
394 | // Give wl_output.done and zxdg_output_v1.done events a chance to arrive |
395 | forceRoundTrip(); |
396 | } |
397 | if (mWaylandInputContextRequested) |
398 | mTextInputManagerIndex = INT_MAX; |
399 | |
400 | return qEnvironmentVariableIntValue(varName: "QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration(); |
401 | } |
402 | |
403 | void QWaylandDisplay::ensureScreen() |
404 | { |
405 | if (!mScreens.empty() || mPlaceholderScreen) |
406 | return; // There are real screens or we already have a fake one |
407 | |
408 | qCInfo(lcQpaWayland) << "There are no outputs - creating placeholder screen"; |
409 | |
410 | mPlaceholderScreen = new QPlatformPlaceholderScreen(); |
411 | QWindowSystemInterface::handleScreenAdded(screen: mPlaceholderScreen); |
412 | Q_ASSERT(!QGuiApplication::screens().empty()); |
413 | } |
414 | |
415 | void QWaylandDisplay::reconnect() |
416 | { |
417 | qCWarning(lcQpaWayland) << "Attempting wayland reconnect"; |
418 | m_eventThread->stop(); |
419 | m_frameEventQueueThread->stop(); |
420 | m_eventThread->wait(); |
421 | m_frameEventQueueThread->wait(); |
422 | |
423 | qDeleteAll(c: mWaitingScreens); |
424 | mWaitingScreens.clear(); |
425 | |
426 | while (!mScreens.isEmpty()) { |
427 | auto screen = mScreens.takeLast(); |
428 | ensureScreen(); |
429 | QWindowSystemInterface::handleScreenRemoved(screen); |
430 | } |
431 | |
432 | mCursorThemes.clear(); |
433 | mCursor.reset(); |
434 | |
435 | mGlobals = GlobalHolder(); |
436 | |
437 | mWaylandIntegration->reset(); |
438 | |
439 | qDeleteAll(c: std::exchange(obj&: mInputDevices, new_val: {})); |
440 | mLastInputDevice = nullptr; |
441 | |
442 | for (const RegistryGlobal &global : mRegistryGlobals) { |
443 | emit globalRemoved(global); |
444 | } |
445 | mRegistryGlobals.clear(); |
446 | |
447 | mLastInputSerial = 0; |
448 | mLastInputWindow.clear(); |
449 | mLastKeyboardFocus.clear(); |
450 | mActiveWindows.clear(); |
451 | |
452 | const auto windows = QGuiApplication::allWindows(); |
453 | for (auto window : windows) { |
454 | if (auto waylandWindow = static_cast<QWaylandWindow *>(window->handle())) |
455 | waylandWindow->closeChildPopups(); |
456 | } |
457 | // Remove windows that do not need to be recreated and now closed popups |
458 | QList<QWaylandWindow *> recreateWindows; |
459 | for (auto window : std::as_const(t: windows)) { |
460 | auto waylandWindow = static_cast<QWaylandWindow*>(window->handle()); |
461 | if (waylandWindow && waylandWindow->wlSurface()) { |
462 | waylandWindow->reset(); |
463 | recreateWindows.push_back(t: waylandWindow); |
464 | } |
465 | } |
466 | |
467 | if (mSyncCallback) { |
468 | wl_callback_destroy(mSyncCallback); |
469 | mSyncCallback = nullptr; |
470 | } |
471 | |
472 | if (object()) |
473 | wl_registry_destroy(object()); |
474 | mDisplay = wl_display_connect(nullptr); |
475 | if (!mDisplay) |
476 | _exit(status: 1); |
477 | |
478 | setupConnection(); |
479 | initialize(); |
480 | |
481 | if (m_frameEventQueue) |
482 | wl_event_queue_destroy(m_frameEventQueue); |
483 | initEventThread(); |
484 | |
485 | auto needsRecreate = [](QPlatformWindow *window) { |
486 | return window && !static_cast<QWaylandWindow *>(window)->wlSurface(); |
487 | }; |
488 | auto window = recreateWindows.begin(); |
489 | while (!recreateWindows.isEmpty()) { |
490 | if (!needsRecreate((*window)->QPlatformWindow::parent()) && !needsRecreate((*window)->transientParent())) { |
491 | (*window)->reinit(); |
492 | window = recreateWindows.erase(pos: window); |
493 | } else { |
494 | ++window; |
495 | } |
496 | if (window == recreateWindows.end()) |
497 | window = recreateWindows.begin(); |
498 | } |
499 | |
500 | mWaylandIntegration->reconfigureInputContext(); |
501 | } |
502 | |
503 | void QWaylandDisplay::flushRequests() |
504 | { |
505 | m_eventThread->readAndDispatchEvents(); |
506 | } |
507 | |
508 | // We have to wait until we have an eventDispatcher before creating the eventThread, |
509 | // otherwise forceRoundTrip() may block inside _events_read() because eventThread is |
510 | // polling. |
511 | void QWaylandDisplay::initEventThread() |
512 | { |
513 | m_eventThread.reset( |
514 | p: new EventThread(mDisplay, /* default queue */ nullptr, EventThread::EmitToDispatch)); |
515 | connect(m_eventThread.get(), &EventThread::needReadAndDispatch, this, |
516 | &QWaylandDisplay::flushRequests, Qt::QueuedConnection); |
517 | connect(m_eventThread.get(), &EventThread::waylandError, this, |
518 | &QWaylandDisplay::checkWaylandError, Qt::QueuedConnection); |
519 | m_eventThread->start(); |
520 | |
521 | // wl_display_disconnect() free this. |
522 | m_frameEventQueue = wl_display_create_queue(mDisplay); |
523 | m_frameEventQueueThread.reset( |
524 | new EventThread(mDisplay, m_frameEventQueue, EventThread::SelfDispatch)); |
525 | m_frameEventQueueThread->start(); |
526 | } |
527 | |
528 | void QWaylandDisplay::checkWaylandError() |
529 | { |
530 | int ecode = wl_display_get_error(mDisplay); |
531 | if ((ecode == EPIPE || ecode == ECONNRESET)) { |
532 | qWarning(msg: "The Wayland connection broke. Did the Wayland compositor die?"); |
533 | if (mWaylandTryReconnect) { |
534 | reconnect(); |
535 | return; |
536 | } |
537 | } else { |
538 | qWarning(msg: "The Wayland connection experienced a fatal error: %s", strerror(errnum: ecode)); |
539 | } |
540 | _exit(status: -1); |
541 | } |
542 | |
543 | void QWaylandDisplay::blockingReadEvents() |
544 | { |
545 | if (wl_display_dispatch(mDisplay) < 0) { |
546 | int ecode = wl_display_get_error(mDisplay); |
547 | if ((ecode == EPIPE || ecode == ECONNRESET)) |
548 | qWarning(msg: "The Wayland connection broke during blocking read event. Did the Wayland compositor die?"); |
549 | else |
550 | qWarning(msg: "The Wayland connection experienced a fatal error during blocking read event: %s", strerror(errnum: ecode)); |
551 | _exit(status: -1); |
552 | } |
553 | } |
554 | |
555 | void QWaylandDisplay::checkTextInputProtocol() |
556 | { |
557 | QStringList tips, timps; // for text input protocols and text input manager protocols |
558 | // zwp_text_input_v2 is preferred over zwp_text_input_v3 because: |
559 | // - Currently, v3 is not as feature rich as v2. |
560 | // - While v2 is not upstreamed, it is well supported by KWin since Plasma 5 and Plasma |
561 | // Mobile uses some v2 only. |
562 | tips << QLatin1String(QtWayland::qt_text_input_method_v1::interface()->name) |
563 | << QLatin1String(QtWayland::zwp_text_input_v2::interface()->name) |
564 | << QLatin1String(QtWayland::zwp_text_input_v3::interface()->name) |
565 | << QLatin1String(QtWayland::zwp_text_input_v1::interface()->name); |
566 | timps << QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name) |
567 | << QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name) |
568 | << QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name) |
569 | << QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name); |
570 | |
571 | QString tiProtocols = QString::fromLocal8Bit(ba: qgetenv(varName: "QT_WAYLAND_TEXT_INPUT_PROTOCOL")); |
572 | qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL="<< tiProtocols; |
573 | QStringList keys; |
574 | if (!tiProtocols.isEmpty()) { |
575 | keys = tiProtocols.split(sep: QLatin1Char(';')); |
576 | QList<QString>::iterator it = keys.begin(); |
577 | while (it != keys.end()) { |
578 | if (tips.contains(str: *it)) |
579 | mTextInputManagerList.append(t: timps.at(i: tips.indexOf(str: *it))); |
580 | else |
581 | qCDebug(lcQpaWayland) << "text input: unknown protocol - "<< *it; |
582 | ++it; |
583 | } |
584 | } |
585 | if (mTextInputManagerList.isEmpty()) // fallback |
586 | mTextInputManagerList = timps; |
587 | } |
588 | |
589 | QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const |
590 | { |
591 | for (auto screen : std::as_const(t: mScreens)) { |
592 | if (screen->output() == output) |
593 | return screen; |
594 | } |
595 | return nullptr; |
596 | } |
597 | |
598 | void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen) |
599 | { |
600 | if (!mWaitingScreens.removeOne(t: screen)) |
601 | return; |
602 | mScreens.append(t: screen); |
603 | QWindowSystemInterface::handleScreenAdded(screen); |
604 | if (mPlaceholderScreen) { |
605 | // handleScreenRemoved deletes the platform screen |
606 | QPlatformScreen *s = mPlaceholderScreen; |
607 | mPlaceholderScreen = nullptr; |
608 | QWindowSystemInterface::handleScreenRemoved(screen: s); |
609 | |
610 | } |
611 | } |
612 | |
613 | template <typename T, auto f> |
614 | struct WithDestructor : public T |
615 | { |
616 | using T::T; |
617 | ~WithDestructor() |
618 | { |
619 | f(this->object()); |
620 | } |
621 | }; |
622 | |
623 | void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uint32_t version) |
624 | { |
625 | struct ::wl_registry *registry = object(); |
626 | |
627 | static QStringList interfaceBlacklist = qEnvironmentVariable(varName: "QT_WAYLAND_DISABLED_INTERFACES").split(sep: u','); |
628 | if (interfaceBlacklist.contains(str: interface)) { |
629 | return; |
630 | } |
631 | |
632 | if (interface == QLatin1String(QtWayland::wl_output::interface()->name)) { |
633 | mWaitingScreens << mWaylandIntegration->createPlatformScreen(waylandDisplay: this, version, id); |
634 | } else if (interface == QLatin1String(QtWayland::wl_compositor::interface()->name)) { |
635 | mGlobals.compositor.reset( |
636 | new WithDestructor<QtWayland::wl_compositor, wl_compositor_destroy>( |
637 | registry, id, qMin((int)version, 6))); |
638 | } else if (interface == QLatin1String(QWaylandShm::interface()->name)) { |
639 | mGlobals.shm.reset(new QWaylandShm(this, version, id)); |
640 | } else if (interface == QLatin1String(QWaylandInputDevice::interface()->name)) { |
641 | QWaylandInputDevice *inputDevice = mWaylandIntegration->createInputDevice(display: this, version, id); |
642 | mInputDevices.append(t: inputDevice); |
643 | #if QT_CONFIG(wayland_datadevice) |
644 | } else if (interface == QLatin1String(QWaylandDataDeviceManager::interface()->name)) { |
645 | mGlobals.dndSelectionHandler.reset(new QWaylandDataDeviceManager(this, version, id)); |
646 | #endif |
647 | } else if (interface == QLatin1String(QtWayland::qt_surface_extension::interface()->name)) { |
648 | mGlobals.surfaceExtension.reset( |
649 | new WithDestructor<QtWayland::qt_surface_extension, qt_surface_extension_destroy>( |
650 | registry, id, 1)); |
651 | } else if (interface == QLatin1String(QtWayland::wl_subcompositor::interface()->name)) { |
652 | mGlobals.subCompositor.reset( |
653 | new WithDestructor<QtWayland::wl_subcompositor, wl_subcompositor_destroy>(registry, |
654 | id, 1)); |
655 | } else if (interface == QLatin1String(QWaylandTouchExtension::interface()->name)) { |
656 | mGlobals.touchExtension.reset(new QWaylandTouchExtension(this, id)); |
657 | } else if (interface == QLatin1String(QWaylandQtKeyExtension::interface()->name)) { |
658 | mGlobals.qtKeyExtension.reset(new QWaylandQtKeyExtension(this, id)); |
659 | #if QT_CONFIG(tabletevent) |
660 | } else if (interface == QLatin1String(QWaylandTabletManagerV2::interface()->name)) { |
661 | mGlobals.tabletManager.reset(new QWaylandTabletManagerV2(this, id, qMin(1, int(version)))); |
662 | for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) |
663 | inputDevice->setTabletSeat( |
664 | new QWaylandTabletSeatV2(mGlobals.tabletManager.get(), inputDevice)); |
665 | #endif |
666 | } else if (interface == QLatin1String(QWaylandPointerGestures::interface()->name)) { |
667 | mGlobals.pointerGestures.reset(new QWaylandPointerGestures(this, id, 1)); |
668 | #if QT_CONFIG(wayland_client_primary_selection) |
669 | } else if (interface == QLatin1String(QWaylandPrimarySelectionDeviceManagerV1::interface()->name)) { |
670 | mGlobals.primarySelectionManager.reset( |
671 | new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1)); |
672 | for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) |
673 | inputDevice->setPrimarySelectionDevice( |
674 | mGlobals.primarySelectionManager->createDevice(inputDevice)); |
675 | #endif |
676 | } else if (interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name) |
677 | && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { |
678 | qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1"; |
679 | if (mTextInputManagerIndex < INT_MAX) { |
680 | mGlobals.textInputManagerv1.reset(); |
681 | mGlobals.textInputManagerv2.reset(); |
682 | mGlobals.textInputManagerv3.reset(); |
683 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
684 | inputDevice->setTextInput(nullptr); |
685 | } |
686 | |
687 | mGlobals.textInputMethodManager.reset( |
688 | new WithDestructor<QtWayland::qt_text_input_method_manager_v1, |
689 | qt_text_input_method_manager_v1_destroy>(registry, id, 1)); |
690 | for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) |
691 | inputDevice->setTextInputMethod(new QWaylandTextInputMethod( |
692 | this, |
693 | mGlobals.textInputMethodManager->get_text_input_method( |
694 | inputDevice->wl_seat()))); |
695 | mWaylandIntegration->reconfigureInputContext(); |
696 | mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface); |
697 | } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name) |
698 | && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { |
699 | qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1"; |
700 | if (mTextInputManagerIndex < INT_MAX) { |
701 | mGlobals.textInputMethodManager.reset(); |
702 | mGlobals.textInputManagerv2.reset(); |
703 | mGlobals.textInputManagerv3.reset(); |
704 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
705 | inputDevice->setTextInputMethod(nullptr); |
706 | } |
707 | |
708 | mGlobals.textInputManagerv1.reset( |
709 | new WithDestructor<QtWayland::zwp_text_input_manager_v1, |
710 | zwp_text_input_manager_v1_destroy>(registry, id, 1)); |
711 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) { |
712 | auto textInput = |
713 | new QWaylandTextInputv1(this, mGlobals.textInputManagerv1->create_text_input()); |
714 | textInput->setSeat(inputDevice->wl_seat()); |
715 | inputDevice->setTextInput(textInput); |
716 | } |
717 | |
718 | mWaylandIntegration->reconfigureInputContext(); |
719 | mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface); |
720 | } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name) |
721 | && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { |
722 | qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2"; |
723 | if (mTextInputManagerIndex < INT_MAX) { |
724 | mGlobals.textInputMethodManager.reset(); |
725 | mGlobals.textInputManagerv1.reset(); |
726 | mGlobals.textInputManagerv3.reset(); |
727 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
728 | inputDevice->setTextInputMethod(nullptr); |
729 | } |
730 | |
731 | mGlobals.textInputManagerv2.reset( |
732 | new WithDestructor<QtWayland::zwp_text_input_manager_v2, |
733 | zwp_text_input_manager_v2_destroy>(registry, id, 1)); |
734 | for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) |
735 | inputDevice->setTextInput(new QWaylandTextInputv2( |
736 | this, mGlobals.textInputManagerv2->get_text_input(inputDevice->wl_seat()))); |
737 | mWaylandIntegration->reconfigureInputContext(); |
738 | mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface); |
739 | } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name) |
740 | && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { |
741 | qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3"; |
742 | if (mTextInputManagerIndex < INT_MAX) { |
743 | mGlobals.textInputMethodManager.reset(); |
744 | mGlobals.textInputManagerv2.reset(); |
745 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
746 | inputDevice->setTextInputMethod(nullptr); |
747 | } |
748 | mGlobals.textInputManagerv3.reset( |
749 | new WithDestructor<QtWayland::zwp_text_input_manager_v3, |
750 | zwp_text_input_manager_v3_destroy>(registry, id, 1)); |
751 | for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) |
752 | inputDevice->setTextInput(new QWaylandTextInputv3( |
753 | this, mGlobals.textInputManagerv3->get_text_input(inputDevice->wl_seat()))); |
754 | |
755 | mWaylandIntegration->reconfigureInputContext(); |
756 | mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface); |
757 | }else if (interface == QLatin1String(QWaylandHardwareIntegration::interface()->name)) { |
758 | bool disableHardwareIntegration = qEnvironmentVariableIntValue(varName: "QT_WAYLAND_DISABLE_HW_INTEGRATION"); |
759 | if (!disableHardwareIntegration) { |
760 | mGlobals.hardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id)); |
761 | // make a roundtrip here since we need to receive the events sent by |
762 | // qt_hardware_integration before creating windows |
763 | forceRoundTrip(); |
764 | } |
765 | } else if (interface == QLatin1String(QWaylandXdgOutputManagerV1::interface()->name)) { |
766 | mGlobals.xdgOutputManager.reset(new QWaylandXdgOutputManagerV1(this, id, version)); |
767 | for (auto *screen : std::as_const(t&: mWaitingScreens)) |
768 | screen->initXdgOutput(xdgOutputManager: xdgOutputManager()); |
769 | } else if (interface == QLatin1String(QtWayland::wp_fractional_scale_manager_v1::interface()->name)) { |
770 | mGlobals.fractionalScaleManager.reset( |
771 | new WithDestructor<QtWayland::wp_fractional_scale_manager_v1, |
772 | wp_fractional_scale_manager_v1_destroy>(registry, id, 1)); |
773 | } else if (interface == QLatin1String("wp_viewporter")) { |
774 | mGlobals.viewporter.reset( |
775 | new WithDestructor<QtWayland::wp_viewporter, wp_viewporter_destroy>( |
776 | registry, id, qMin(1u, version))); |
777 | } else if (interface == QLatin1String(QtWayland::wp_cursor_shape_manager_v1::interface()->name)) { |
778 | mGlobals.cursorShapeManager.reset(new WithDestructor<QtWayland::wp_cursor_shape_manager_v1, |
779 | wp_cursor_shape_manager_v1_destroy>( |
780 | registry, id, std::min(1u, version))); |
781 | } else if ( |
782 | interface == QLatin1String(QtWayland::xdg_toplevel_drag_manager_v1::interface()->name)) { |
783 | mGlobals.xdgToplevelDragManager.reset( |
784 | new WithDestructor<QtWayland::xdg_toplevel_drag_manager_v1, |
785 | xdg_toplevel_drag_manager_v1_destroy>(registry, id, 1)); |
786 | } else if (interface == QLatin1String(QtWayland::qt_windowmanager::interface()->name)) { |
787 | mGlobals.windowManagerIntegration.reset( |
788 | new QWaylandWindowManagerIntegration(this, id, version)); |
789 | } |
790 | |
791 | mRegistryGlobals.append(t: RegistryGlobal(id, interface, version, registry)); |
792 | emit globalAdded(global: mRegistryGlobals.back()); |
793 | |
794 | const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification |
795 | for (Listener l : copy) |
796 | (*l.listener)(l.data, registry, id, interface, version); |
797 | } |
798 | |
799 | void QWaylandDisplay::registry_global_remove(uint32_t id) |
800 | { |
801 | for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) { |
802 | RegistryGlobal &global = mRegistryGlobals[i]; |
803 | if (global.id == id) { |
804 | if (global.interface == QLatin1String(QtWayland::wl_output::interface()->name)) { |
805 | for (auto *screen : mWaitingScreens) { |
806 | if (screen->outputId() == id) { |
807 | mWaitingScreens.removeOne(t: screen); |
808 | delete screen; |
809 | break; |
810 | } |
811 | } |
812 | |
813 | for (QWaylandScreen *screen : std::as_const(t&: mScreens)) { |
814 | if (screen->outputId() == id) { |
815 | mScreens.removeOne(t: screen); |
816 | // If this is the last screen, we have to add a fake screen, or Qt will break. |
817 | ensureScreen(); |
818 | QWindowSystemInterface::handleScreenRemoved(screen); |
819 | break; |
820 | } |
821 | } |
822 | } |
823 | if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)) { |
824 | mGlobals.textInputManagerv1.reset(); |
825 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
826 | inputDevice->setTextInput(nullptr); |
827 | mWaylandIntegration->reconfigureInputContext(); |
828 | } |
829 | if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)) { |
830 | mGlobals.textInputManagerv2.reset(); |
831 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
832 | inputDevice->setTextInput(nullptr); |
833 | mWaylandIntegration->reconfigureInputContext(); |
834 | } |
835 | if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)) { |
836 | mGlobals.textInputManagerv3.reset(); |
837 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
838 | inputDevice->setTextInput(nullptr); |
839 | mWaylandIntegration->reconfigureInputContext(); |
840 | } |
841 | if (global.interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)) { |
842 | mGlobals.textInputMethodManager.reset(); |
843 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
844 | inputDevice->setTextInputMethod(nullptr); |
845 | mWaylandIntegration->reconfigureInputContext(); |
846 | } |
847 | #if QT_CONFIG(wayland_client_primary_selection) |
848 | if (global.interface == QLatin1String(QtWayland::zwp_primary_selection_device_manager_v1::interface()->name)) { |
849 | mGlobals.primarySelectionManager.reset(); |
850 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
851 | inputDevice->setPrimarySelectionDevice(nullptr); |
852 | } |
853 | #endif |
854 | emit globalRemoved(global: mRegistryGlobals.takeAt(i)); |
855 | break; |
856 | } |
857 | } |
858 | } |
859 | |
860 | bool QWaylandDisplay::hasRegistryGlobal(QStringView interfaceName) const |
861 | { |
862 | for (const RegistryGlobal &global : mRegistryGlobals) |
863 | if (global.interface == interfaceName) |
864 | return true; |
865 | |
866 | return false; |
867 | } |
868 | |
869 | void QWaylandDisplay::addRegistryListener(RegistryListener listener, void *data) |
870 | { |
871 | Listener l = { listener, data }; |
872 | mRegistryListeners.append(t: l); |
873 | for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) |
874 | (*l.listener)(l.data, mRegistryGlobals[i].registry, mRegistryGlobals[i].id, |
875 | mRegistryGlobals[i].interface, mRegistryGlobals[i].version); |
876 | } |
877 | |
878 | void QWaylandDisplay::removeListener(RegistryListener listener, void *data) |
879 | { |
880 | auto iter = std::remove_if(first: mRegistryListeners.begin(), last: mRegistryListeners.end(), pred: [=](Listener l){ |
881 | return (l.listener == listener && l.data == data); |
882 | }); |
883 | mRegistryListeners.erase(abegin: iter, aend: mRegistryListeners.end()); |
884 | } |
885 | |
886 | void QWaylandDisplay::forceRoundTrip() |
887 | { |
888 | wl_display_roundtrip(mDisplay); |
889 | } |
890 | |
891 | bool QWaylandDisplay::supportsWindowDecoration() const |
892 | { |
893 | static bool disabled = qgetenv(varName: "QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt(); |
894 | // Stop early when disabled via the environment. Do not try to load the integration in |
895 | // order to play nice with SHM-only, buffer integration-less systems. |
896 | if (disabled) |
897 | return false; |
898 | |
899 | // Don't initialize client buffer integration just to check whether it can have a decoration. |
900 | if (!mWaylandIntegration->mClientBufferIntegrationInitialized) |
901 | return true; |
902 | |
903 | // We can do software-rendered decorations, only disable them if the integration explicitly says it can't. |
904 | static bool integrationSupport = !clientBufferIntegration() || clientBufferIntegration()->supportsWindowDecoration(); |
905 | return integrationSupport; |
906 | } |
907 | |
908 | QWaylandWindow *QWaylandDisplay::lastInputWindow() const |
909 | { |
910 | return mLastInputWindow.data(); |
911 | } |
912 | |
913 | void QWaylandDisplay::setLastInputDevice(QWaylandInputDevice *device, uint32_t serial, QWaylandWindow *win) |
914 | { |
915 | mLastInputDevice = device; |
916 | mLastInputSerial = serial; |
917 | mLastInputWindow = win; |
918 | } |
919 | |
920 | bool QWaylandDisplay::isWindowActivated(const QWaylandWindow *window) |
921 | { |
922 | return mActiveWindows.contains(t: const_cast<QWaylandWindow *>(window)); |
923 | } |
924 | |
925 | void QWaylandDisplay::handleWindowActivated(QWaylandWindow *window) |
926 | { |
927 | if (mActiveWindows.contains(t: window)) |
928 | return; |
929 | |
930 | mActiveWindows.append(t: window); |
931 | requestWaylandSync(); |
932 | |
933 | if (auto *decoration = window->decoration()) |
934 | decoration->update(); |
935 | } |
936 | |
937 | void QWaylandDisplay::handleWindowDeactivated(QWaylandWindow *window) |
938 | { |
939 | Q_ASSERT(!mActiveWindows.empty()); |
940 | |
941 | if (mActiveWindows.last() == window) |
942 | requestWaylandSync(); |
943 | |
944 | mActiveWindows.removeOne(t: window); |
945 | |
946 | if (QCoreApplication::closingDown()) |
947 | return; |
948 | |
949 | if (auto *decoration = window->decoration()) |
950 | decoration->update(); |
951 | } |
952 | |
953 | void QWaylandDisplay::handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice) |
954 | { |
955 | QWaylandWindow *keyboardFocus = inputDevice->keyboardFocus(); |
956 | |
957 | if (mLastKeyboardFocus == keyboardFocus) |
958 | return; |
959 | |
960 | if (keyboardFocus) |
961 | handleWindowActivated(window: keyboardFocus); |
962 | if (mLastKeyboardFocus) |
963 | handleWindowDeactivated(window: mLastKeyboardFocus); |
964 | |
965 | mLastKeyboardFocus = keyboardFocus; |
966 | } |
967 | |
968 | void QWaylandDisplay::handleWindowDestroyed(QWaylandWindow *window) |
969 | { |
970 | if (mActiveWindows.contains(t: window)) |
971 | handleWindowDeactivated(window); |
972 | } |
973 | |
974 | void QWaylandDisplay::handleWaylandSync() |
975 | { |
976 | // This callback is used to set the window activation because we may get an activate/deactivate |
977 | // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the |
978 | // handleWindowActivated() calls immediately. |
979 | QWindow *activeWindow = mActiveWindows.empty() ? nullptr : mActiveWindows.last()->window(); |
980 | if (activeWindow != QGuiApplication::focusWindow()) |
981 | QWindowSystemInterface::handleFocusWindowChanged(window: activeWindow); |
982 | |
983 | if (!activeWindow) { |
984 | if (lastInputDevice()) { |
985 | #if QT_CONFIG(clipboard) |
986 | if (auto *dataDevice = lastInputDevice()->dataDevice()) |
987 | dataDevice->invalidateSelectionOffer(); |
988 | #endif |
989 | #if QT_CONFIG(wayland_client_primary_selection) |
990 | if (auto *device = lastInputDevice()->primarySelectionDevice()) |
991 | device->invalidateSelectionOffer(); |
992 | #endif |
993 | } |
994 | } |
995 | } |
996 | |
997 | const wl_callback_listener QWaylandDisplay::syncCallbackListener = { |
998 | [](void *data, struct wl_callback *callback, uint32_t time){ |
999 | Q_UNUSED(time); |
1000 | wl_callback_destroy(callback); |
1001 | QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data); |
1002 | display->mSyncCallback = nullptr; |
1003 | display->handleWaylandSync(); |
1004 | } |
1005 | }; |
1006 | |
1007 | void QWaylandDisplay::requestWaylandSync() |
1008 | { |
1009 | if (mSyncCallback) |
1010 | return; |
1011 | |
1012 | mSyncCallback = wl_display_sync(mDisplay); |
1013 | wl_callback_add_listener(mSyncCallback, &syncCallbackListener, this); |
1014 | } |
1015 | |
1016 | QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const |
1017 | { |
1018 | return mInputDevices.isEmpty() ? nullptr : mInputDevices.first(); |
1019 | } |
1020 | |
1021 | bool QWaylandDisplay::isKeyboardAvailable() const |
1022 | { |
1023 | return std::any_of( |
1024 | first: mInputDevices.constBegin(), last: mInputDevices.constEnd(), |
1025 | pred: [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; }); |
1026 | } |
1027 | |
1028 | bool QWaylandDisplay::isWaylandInputContextRequested() const { |
1029 | return mWaylandInputContextRequested; |
1030 | } |
1031 | |
1032 | #if QT_CONFIG(cursor) |
1033 | |
1034 | QWaylandCursor *QWaylandDisplay::waylandCursor() |
1035 | { |
1036 | if (!mCursor) |
1037 | mCursor.reset(other: mWaylandIntegration->createPlatformCursor(display: this)); |
1038 | return mCursor.data(); |
1039 | } |
1040 | |
1041 | auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept |
1042 | -> FindExistingCursorThemeResult |
1043 | { |
1044 | const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) { |
1045 | return std::tie(args: lhs.pixelSize, args: lhs.name) < std::tie(args: rhs.pixelSize, args: rhs.name); |
1046 | }; |
1047 | |
1048 | const WaylandCursorTheme prototype = {.name: name, .pixelSize: pixelSize, .theme: nullptr}; |
1049 | |
1050 | const auto it = std::lower_bound(first: mCursorThemes.cbegin(), last: mCursorThemes.cend(), val: prototype, comp: byNameAndSize); |
1051 | if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize) |
1052 | return {.position: it, .found: true}; |
1053 | else |
1054 | return {.position: it, .found: false}; |
1055 | } |
1056 | |
1057 | QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int pixelSize) |
1058 | { |
1059 | const auto result = findExistingCursorTheme(name, pixelSize); |
1060 | if (result.found) |
1061 | return result.theme(); |
1062 | |
1063 | if (auto theme = QWaylandCursorTheme::create(shm: shm(), size: pixelSize, themeName: name)) |
1064 | return mCursorThemes.insert(position: result.position, x: {.name: name, .pixelSize: pixelSize, .theme: std::move(theme)})->theme.get(); |
1065 | |
1066 | return nullptr; |
1067 | } |
1068 | |
1069 | #endif // QT_CONFIG(cursor) |
1070 | |
1071 | } // namespace QtWaylandClient |
1072 | |
1073 | QT_END_NAMESPACE |
1074 | |
1075 | #include "qwaylanddisplay.moc" |
1076 | #include "moc_qwaylanddisplay_p.cpp" |
1077 |
Definitions
- EventThread
- OperatingMode
- EventThread
- readAndDispatchEvents
- stop
- run
- waitForReading
- dispatchQueuePending
- prepareReadQueue
- lcQpaWayland
- createSurface
- createRegion
- createSubSurface
- createViewport
- shellIntegration
- clientBufferIntegration
- windowManagerIntegration
- QWaylandDisplay
- setupConnection
- ~QWaylandDisplay
- initialize
- ensureScreen
- reconnect
- flushRequests
- initEventThread
- checkWaylandError
- blockingReadEvents
- checkTextInputProtocol
- screenForOutput
- handleScreenInitialized
- WithDestructor
- ~WithDestructor
- registry_global
- registry_global_remove
- hasRegistryGlobal
- addRegistryListener
- removeListener
- forceRoundTrip
- supportsWindowDecoration
- lastInputWindow
- setLastInputDevice
- isWindowActivated
- handleWindowActivated
- handleWindowDeactivated
- handleKeyboardFocusChanged
- handleWindowDestroyed
- handleWaylandSync
- syncCallbackListener
- requestWaylandSync
- defaultInputDevice
- isKeyboardAvailable
- isWaylandInputContextRequested
- waylandCursor
- findExistingCursorTheme
Learn to use CMake with our Intro Training
Find out more