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) << "Creating a fake screen in order for Qt not to crash"; |
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 | QWindowSystemInterface::handleScreenRemoved(screen: mPlaceholderScreen); |
606 | // handleScreenRemoved deletes the platform screen |
607 | mPlaceholderScreen = nullptr; |
608 | } |
609 | } |
610 | |
611 | template <typename T, auto f> |
612 | struct WithDestructor : public T |
613 | { |
614 | using T::T; |
615 | ~WithDestructor() |
616 | { |
617 | f(this->object()); |
618 | } |
619 | }; |
620 | |
621 | void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uint32_t version) |
622 | { |
623 | struct ::wl_registry *registry = object(); |
624 | |
625 | static QStringList interfaceBlacklist = qEnvironmentVariable(varName: "QT_WAYLAND_DISABLED_INTERFACES").split(sep: u','); |
626 | if (interfaceBlacklist.contains(str: interface)) { |
627 | return; |
628 | } |
629 | |
630 | if (interface == QLatin1String(QtWayland::wl_output::interface()->name)) { |
631 | mWaitingScreens << mWaylandIntegration->createPlatformScreen(waylandDisplay: this, version, id); |
632 | } else if (interface == QLatin1String(QtWayland::wl_compositor::interface()->name)) { |
633 | mGlobals.compositor.reset( |
634 | new WithDestructor<QtWayland::wl_compositor, wl_compositor_destroy>( |
635 | registry, id, qMin((int)version, 6))); |
636 | } else if (interface == QLatin1String(QWaylandShm::interface()->name)) { |
637 | mGlobals.shm.reset(new QWaylandShm(this, version, id)); |
638 | } else if (interface == QLatin1String(QWaylandInputDevice::interface()->name)) { |
639 | QWaylandInputDevice *inputDevice = mWaylandIntegration->createInputDevice(display: this, version, id); |
640 | mInputDevices.append(t: inputDevice); |
641 | #if QT_CONFIG(wayland_datadevice) |
642 | } else if (interface == QLatin1String(QWaylandDataDeviceManager::interface()->name)) { |
643 | mGlobals.dndSelectionHandler.reset(new QWaylandDataDeviceManager(this, version, id)); |
644 | #endif |
645 | } else if (interface == QLatin1String(QtWayland::qt_surface_extension::interface()->name)) { |
646 | mGlobals.surfaceExtension.reset( |
647 | new WithDestructor<QtWayland::qt_surface_extension, qt_surface_extension_destroy>( |
648 | registry, id, 1)); |
649 | } else if (interface == QLatin1String(QtWayland::wl_subcompositor::interface()->name)) { |
650 | mGlobals.subCompositor.reset( |
651 | new WithDestructor<QtWayland::wl_subcompositor, wl_subcompositor_destroy>(registry, |
652 | id, 1)); |
653 | } else if (interface == QLatin1String(QWaylandTouchExtension::interface()->name)) { |
654 | mGlobals.touchExtension.reset(new QWaylandTouchExtension(this, id)); |
655 | } else if (interface == QLatin1String(QWaylandQtKeyExtension::interface()->name)) { |
656 | mGlobals.qtKeyExtension.reset(new QWaylandQtKeyExtension(this, id)); |
657 | #if QT_CONFIG(tabletevent) |
658 | } else if (interface == QLatin1String(QWaylandTabletManagerV2::interface()->name)) { |
659 | mGlobals.tabletManager.reset(new QWaylandTabletManagerV2(this, id, qMin(1, int(version)))); |
660 | for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) |
661 | inputDevice->setTabletSeat( |
662 | new QWaylandTabletSeatV2(mGlobals.tabletManager.get(), inputDevice)); |
663 | #endif |
664 | } else if (interface == QLatin1String(QWaylandPointerGestures::interface()->name)) { |
665 | mGlobals.pointerGestures.reset(new QWaylandPointerGestures(this, id, 1)); |
666 | #if QT_CONFIG(wayland_client_primary_selection) |
667 | } else if (interface == QLatin1String(QWaylandPrimarySelectionDeviceManagerV1::interface()->name)) { |
668 | mGlobals.primarySelectionManager.reset( |
669 | new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1)); |
670 | for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) |
671 | inputDevice->setPrimarySelectionDevice( |
672 | mGlobals.primarySelectionManager->createDevice(inputDevice)); |
673 | #endif |
674 | } else if (interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name) |
675 | && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { |
676 | qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1"; |
677 | if (mTextInputManagerIndex < INT_MAX) { |
678 | mGlobals.textInputManagerv1.reset(); |
679 | mGlobals.textInputManagerv2.reset(); |
680 | mGlobals.textInputManagerv3.reset(); |
681 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
682 | inputDevice->setTextInput(nullptr); |
683 | } |
684 | |
685 | mGlobals.textInputMethodManager.reset( |
686 | new WithDestructor<QtWayland::qt_text_input_method_manager_v1, |
687 | qt_text_input_method_manager_v1_destroy>(registry, id, 1)); |
688 | for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) |
689 | inputDevice->setTextInputMethod(new QWaylandTextInputMethod( |
690 | this, |
691 | mGlobals.textInputMethodManager->get_text_input_method( |
692 | inputDevice->wl_seat()))); |
693 | mWaylandIntegration->reconfigureInputContext(); |
694 | mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface); |
695 | } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name) |
696 | && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { |
697 | qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1"; |
698 | if (mTextInputManagerIndex < INT_MAX) { |
699 | mGlobals.textInputMethodManager.reset(); |
700 | mGlobals.textInputManagerv2.reset(); |
701 | mGlobals.textInputManagerv3.reset(); |
702 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
703 | inputDevice->setTextInputMethod(nullptr); |
704 | } |
705 | |
706 | mGlobals.textInputManagerv1.reset( |
707 | new WithDestructor<QtWayland::zwp_text_input_manager_v1, |
708 | zwp_text_input_manager_v1_destroy>(registry, id, 1)); |
709 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) { |
710 | auto textInput = |
711 | new QWaylandTextInputv1(this, mGlobals.textInputManagerv1->create_text_input()); |
712 | textInput->setSeat(inputDevice->wl_seat()); |
713 | inputDevice->setTextInput(textInput); |
714 | } |
715 | |
716 | mWaylandIntegration->reconfigureInputContext(); |
717 | mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface); |
718 | } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name) |
719 | && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { |
720 | qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2"; |
721 | if (mTextInputManagerIndex < INT_MAX) { |
722 | mGlobals.textInputMethodManager.reset(); |
723 | mGlobals.textInputManagerv1.reset(); |
724 | mGlobals.textInputManagerv3.reset(); |
725 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
726 | inputDevice->setTextInputMethod(nullptr); |
727 | } |
728 | |
729 | mGlobals.textInputManagerv2.reset( |
730 | new WithDestructor<QtWayland::zwp_text_input_manager_v2, |
731 | zwp_text_input_manager_v2_destroy>(registry, id, 1)); |
732 | for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) |
733 | inputDevice->setTextInput(new QWaylandTextInputv2( |
734 | this, mGlobals.textInputManagerv2->get_text_input(inputDevice->wl_seat()))); |
735 | mWaylandIntegration->reconfigureInputContext(); |
736 | mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface); |
737 | } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name) |
738 | && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) { |
739 | qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3"; |
740 | if (mTextInputManagerIndex < INT_MAX) { |
741 | mGlobals.textInputMethodManager.reset(); |
742 | mGlobals.textInputManagerv2.reset(); |
743 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
744 | inputDevice->setTextInputMethod(nullptr); |
745 | } |
746 | mGlobals.textInputManagerv3.reset( |
747 | new WithDestructor<QtWayland::zwp_text_input_manager_v3, |
748 | zwp_text_input_manager_v3_destroy>(registry, id, 1)); |
749 | for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) |
750 | inputDevice->setTextInput(new QWaylandTextInputv3( |
751 | this, mGlobals.textInputManagerv3->get_text_input(inputDevice->wl_seat()))); |
752 | |
753 | mWaylandIntegration->reconfigureInputContext(); |
754 | mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface); |
755 | }else if (interface == QLatin1String(QWaylandHardwareIntegration::interface()->name)) { |
756 | bool disableHardwareIntegration = qEnvironmentVariableIntValue(varName: "QT_WAYLAND_DISABLE_HW_INTEGRATION"); |
757 | if (!disableHardwareIntegration) { |
758 | mGlobals.hardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id)); |
759 | // make a roundtrip here since we need to receive the events sent by |
760 | // qt_hardware_integration before creating windows |
761 | forceRoundTrip(); |
762 | } |
763 | } else if (interface == QLatin1String(QWaylandXdgOutputManagerV1::interface()->name)) { |
764 | mGlobals.xdgOutputManager.reset(new QWaylandXdgOutputManagerV1(this, id, version)); |
765 | for (auto *screen : std::as_const(t&: mWaitingScreens)) |
766 | screen->initXdgOutput(xdgOutputManager: xdgOutputManager()); |
767 | } else if (interface == QLatin1String(QtWayland::wp_fractional_scale_manager_v1::interface()->name)) { |
768 | mGlobals.fractionalScaleManager.reset( |
769 | new WithDestructor<QtWayland::wp_fractional_scale_manager_v1, |
770 | wp_fractional_scale_manager_v1_destroy>(registry, id, 1)); |
771 | } else if (interface == QLatin1String("wp_viewporter")) { |
772 | mGlobals.viewporter.reset( |
773 | new WithDestructor<QtWayland::wp_viewporter, wp_viewporter_destroy>( |
774 | registry, id, qMin(1u, version))); |
775 | } else if (interface == QLatin1String(QtWayland::wp_cursor_shape_manager_v1::interface()->name)) { |
776 | mGlobals.cursorShapeManager.reset(new WithDestructor<QtWayland::wp_cursor_shape_manager_v1, |
777 | wp_cursor_shape_manager_v1_destroy>( |
778 | registry, id, std::min(1u, version))); |
779 | } else if ( |
780 | interface == QLatin1String(QtWayland::xdg_toplevel_drag_manager_v1::interface()->name)) { |
781 | mGlobals.xdgToplevelDragManager.reset( |
782 | new WithDestructor<QtWayland::xdg_toplevel_drag_manager_v1, |
783 | xdg_toplevel_drag_manager_v1_destroy>(registry, id, 1)); |
784 | } else if (interface == QLatin1String(QtWayland::qt_windowmanager::interface()->name)) { |
785 | mGlobals.windowManagerIntegration.reset( |
786 | new QWaylandWindowManagerIntegration(this, id, version)); |
787 | } |
788 | |
789 | mRegistryGlobals.append(t: RegistryGlobal(id, interface, version, registry)); |
790 | emit globalAdded(global: mRegistryGlobals.back()); |
791 | |
792 | const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification |
793 | for (Listener l : copy) |
794 | (*l.listener)(l.data, registry, id, interface, version); |
795 | } |
796 | |
797 | void QWaylandDisplay::registry_global_remove(uint32_t id) |
798 | { |
799 | for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) { |
800 | RegistryGlobal &global = mRegistryGlobals[i]; |
801 | if (global.id == id) { |
802 | if (global.interface == QLatin1String(QtWayland::wl_output::interface()->name)) { |
803 | for (auto *screen : mWaitingScreens) { |
804 | if (screen->outputId() == id) { |
805 | mWaitingScreens.removeOne(t: screen); |
806 | delete screen; |
807 | break; |
808 | } |
809 | } |
810 | |
811 | for (QWaylandScreen *screen : std::as_const(t&: mScreens)) { |
812 | if (screen->outputId() == id) { |
813 | mScreens.removeOne(t: screen); |
814 | // If this is the last screen, we have to add a fake screen, or Qt will break. |
815 | ensureScreen(); |
816 | QWindowSystemInterface::handleScreenRemoved(screen); |
817 | break; |
818 | } |
819 | } |
820 | } |
821 | if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)) { |
822 | mGlobals.textInputManagerv1.reset(); |
823 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
824 | inputDevice->setTextInput(nullptr); |
825 | mWaylandIntegration->reconfigureInputContext(); |
826 | } |
827 | if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)) { |
828 | mGlobals.textInputManagerv2.reset(); |
829 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
830 | inputDevice->setTextInput(nullptr); |
831 | mWaylandIntegration->reconfigureInputContext(); |
832 | } |
833 | if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)) { |
834 | mGlobals.textInputManagerv3.reset(); |
835 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
836 | inputDevice->setTextInput(nullptr); |
837 | mWaylandIntegration->reconfigureInputContext(); |
838 | } |
839 | if (global.interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)) { |
840 | mGlobals.textInputMethodManager.reset(); |
841 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
842 | inputDevice->setTextInputMethod(nullptr); |
843 | mWaylandIntegration->reconfigureInputContext(); |
844 | } |
845 | #if QT_CONFIG(wayland_client_primary_selection) |
846 | if (global.interface == QLatin1String(QtWayland::zwp_primary_selection_device_manager_v1::interface()->name)) { |
847 | mGlobals.primarySelectionManager.reset(); |
848 | for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) |
849 | inputDevice->setPrimarySelectionDevice(nullptr); |
850 | } |
851 | #endif |
852 | emit globalRemoved(global: mRegistryGlobals.takeAt(i)); |
853 | break; |
854 | } |
855 | } |
856 | } |
857 | |
858 | bool QWaylandDisplay::hasRegistryGlobal(QStringView interfaceName) const |
859 | { |
860 | for (const RegistryGlobal &global : mRegistryGlobals) |
861 | if (global.interface == interfaceName) |
862 | return true; |
863 | |
864 | return false; |
865 | } |
866 | |
867 | void QWaylandDisplay::addRegistryListener(RegistryListener listener, void *data) |
868 | { |
869 | Listener l = { listener, data }; |
870 | mRegistryListeners.append(t: l); |
871 | for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) |
872 | (*l.listener)(l.data, mRegistryGlobals[i].registry, mRegistryGlobals[i].id, |
873 | mRegistryGlobals[i].interface, mRegistryGlobals[i].version); |
874 | } |
875 | |
876 | void QWaylandDisplay::removeListener(RegistryListener listener, void *data) |
877 | { |
878 | auto iter = std::remove_if(first: mRegistryListeners.begin(), last: mRegistryListeners.end(), pred: [=](Listener l){ |
879 | return (l.listener == listener && l.data == data); |
880 | }); |
881 | mRegistryListeners.erase(abegin: iter, aend: mRegistryListeners.end()); |
882 | } |
883 | |
884 | void QWaylandDisplay::forceRoundTrip() |
885 | { |
886 | wl_display_roundtrip(mDisplay); |
887 | } |
888 | |
889 | bool QWaylandDisplay::supportsWindowDecoration() const |
890 | { |
891 | static bool disabled = qgetenv(varName: "QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt(); |
892 | // Stop early when disabled via the environment. Do not try to load the integration in |
893 | // order to play nice with SHM-only, buffer integration-less systems. |
894 | if (disabled) |
895 | return false; |
896 | |
897 | // Don't initialize client buffer integration just to check whether it can have a decoration. |
898 | if (!mWaylandIntegration->mClientBufferIntegrationInitialized) |
899 | return true; |
900 | |
901 | // We can do software-rendered decorations, only disable them if the integration explicitly says it can't. |
902 | static bool integrationSupport = !clientBufferIntegration() || clientBufferIntegration()->supportsWindowDecoration(); |
903 | return integrationSupport; |
904 | } |
905 | |
906 | QWaylandWindow *QWaylandDisplay::lastInputWindow() const |
907 | { |
908 | return mLastInputWindow.data(); |
909 | } |
910 | |
911 | void QWaylandDisplay::setLastInputDevice(QWaylandInputDevice *device, uint32_t serial, QWaylandWindow *win) |
912 | { |
913 | mLastInputDevice = device; |
914 | mLastInputSerial = serial; |
915 | mLastInputWindow = win; |
916 | } |
917 | |
918 | bool QWaylandDisplay::isWindowActivated(const QWaylandWindow *window) |
919 | { |
920 | return mActiveWindows.contains(t: const_cast<QWaylandWindow *>(window)); |
921 | } |
922 | |
923 | void QWaylandDisplay::handleWindowActivated(QWaylandWindow *window) |
924 | { |
925 | if (mActiveWindows.contains(t: window)) |
926 | return; |
927 | |
928 | mActiveWindows.append(t: window); |
929 | requestWaylandSync(); |
930 | |
931 | if (auto *decoration = window->decoration()) |
932 | decoration->update(); |
933 | } |
934 | |
935 | void QWaylandDisplay::handleWindowDeactivated(QWaylandWindow *window) |
936 | { |
937 | Q_ASSERT(!mActiveWindows.empty()); |
938 | |
939 | if (mActiveWindows.last() == window) |
940 | requestWaylandSync(); |
941 | |
942 | mActiveWindows.removeOne(t: window); |
943 | |
944 | if (QCoreApplication::closingDown()) |
945 | return; |
946 | |
947 | if (auto *decoration = window->decoration()) |
948 | decoration->update(); |
949 | } |
950 | |
951 | void QWaylandDisplay::handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice) |
952 | { |
953 | QWaylandWindow *keyboardFocus = inputDevice->keyboardFocus(); |
954 | |
955 | if (mLastKeyboardFocus == keyboardFocus) |
956 | return; |
957 | |
958 | if (keyboardFocus) |
959 | handleWindowActivated(window: keyboardFocus); |
960 | if (mLastKeyboardFocus) |
961 | handleWindowDeactivated(window: mLastKeyboardFocus); |
962 | |
963 | mLastKeyboardFocus = keyboardFocus; |
964 | } |
965 | |
966 | void QWaylandDisplay::handleWindowDestroyed(QWaylandWindow *window) |
967 | { |
968 | if (mActiveWindows.contains(t: window)) |
969 | handleWindowDeactivated(window); |
970 | } |
971 | |
972 | void QWaylandDisplay::handleWaylandSync() |
973 | { |
974 | // This callback is used to set the window activation because we may get an activate/deactivate |
975 | // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the |
976 | // handleWindowActivated() calls immediately. |
977 | QWindow *activeWindow = mActiveWindows.empty() ? nullptr : mActiveWindows.last()->window(); |
978 | if (activeWindow != QGuiApplication::focusWindow()) |
979 | QWindowSystemInterface::handleFocusWindowChanged(window: activeWindow); |
980 | |
981 | if (!activeWindow) { |
982 | if (lastInputDevice()) { |
983 | #if QT_CONFIG(clipboard) |
984 | if (auto *dataDevice = lastInputDevice()->dataDevice()) |
985 | dataDevice->invalidateSelectionOffer(); |
986 | #endif |
987 | #if QT_CONFIG(wayland_client_primary_selection) |
988 | if (auto *device = lastInputDevice()->primarySelectionDevice()) |
989 | device->invalidateSelectionOffer(); |
990 | #endif |
991 | } |
992 | } |
993 | } |
994 | |
995 | const wl_callback_listener QWaylandDisplay::syncCallbackListener = { |
996 | [](void *data, struct wl_callback *callback, uint32_t time){ |
997 | Q_UNUSED(time); |
998 | wl_callback_destroy(callback); |
999 | QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data); |
1000 | display->mSyncCallback = nullptr; |
1001 | display->handleWaylandSync(); |
1002 | } |
1003 | }; |
1004 | |
1005 | void QWaylandDisplay::requestWaylandSync() |
1006 | { |
1007 | if (mSyncCallback) |
1008 | return; |
1009 | |
1010 | mSyncCallback = wl_display_sync(mDisplay); |
1011 | wl_callback_add_listener(mSyncCallback, &syncCallbackListener, this); |
1012 | } |
1013 | |
1014 | QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const |
1015 | { |
1016 | return mInputDevices.isEmpty() ? nullptr : mInputDevices.first(); |
1017 | } |
1018 | |
1019 | bool QWaylandDisplay::isKeyboardAvailable() const |
1020 | { |
1021 | return std::any_of( |
1022 | first: mInputDevices.constBegin(), last: mInputDevices.constEnd(), |
1023 | pred: [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; }); |
1024 | } |
1025 | |
1026 | bool QWaylandDisplay::isWaylandInputContextRequested() const { |
1027 | return mWaylandInputContextRequested; |
1028 | } |
1029 | |
1030 | #if QT_CONFIG(cursor) |
1031 | |
1032 | QWaylandCursor *QWaylandDisplay::waylandCursor() |
1033 | { |
1034 | if (!mCursor) |
1035 | mCursor.reset(other: mWaylandIntegration->createPlatformCursor(display: this)); |
1036 | return mCursor.data(); |
1037 | } |
1038 | |
1039 | auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept |
1040 | -> FindExistingCursorThemeResult |
1041 | { |
1042 | const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) { |
1043 | return std::tie(args: lhs.pixelSize, args: lhs.name) < std::tie(args: rhs.pixelSize, args: rhs.name); |
1044 | }; |
1045 | |
1046 | const WaylandCursorTheme prototype = {.name: name, .pixelSize: pixelSize, .theme: nullptr}; |
1047 | |
1048 | const auto it = std::lower_bound(first: mCursorThemes.cbegin(), last: mCursorThemes.cend(), val: prototype, comp: byNameAndSize); |
1049 | if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize) |
1050 | return {.position: it, .found: true}; |
1051 | else |
1052 | return {.position: it, .found: false}; |
1053 | } |
1054 | |
1055 | QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int pixelSize) |
1056 | { |
1057 | const auto result = findExistingCursorTheme(name, pixelSize); |
1058 | if (result.found) |
1059 | return result.theme(); |
1060 | |
1061 | if (auto theme = QWaylandCursorTheme::create(shm: shm(), size: pixelSize, themeName: name)) |
1062 | return mCursorThemes.insert(position: result.position, x: {.name: name, .pixelSize: pixelSize, .theme: std::move(theme)})->theme.get(); |
1063 | |
1064 | return nullptr; |
1065 | } |
1066 | |
1067 | #endif // QT_CONFIG(cursor) |
1068 | |
1069 | } // namespace QtWaylandClient |
1070 | |
1071 | QT_END_NAMESPACE |
1072 | |
1073 | #include "qwaylanddisplay.moc" |
1074 | #include "moc_qwaylanddisplay_p.cpp" |
1075 |
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