Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qandroidplatformwindow.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
10
11#include "androidjnimain.h"
12
13#include <qguiapplication.h>
14#include <qpa/qwindowsysteminterface.h>
15#include <qthread.h>
16#include <private/qhighdpiscaling_p.h>
17#include <private/qjnihelpers_p.h>
18
19#include <android/native_window.h>
20#include <android/native_window_jni.h>
21
22#include <memory>
23#include <optional>
24
26
27Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window")
28
29Q_DECLARE_JNI_CLASS(QtWindowInterface, "org/qtproject/qt/android/QtWindowInterface")
30Q_DECLARE_JNI_CLASS(QtInputInterface, "org/qtproject/qt/android/QtInputInterface")
31Q_DECLARE_JNI_CLASS(QtInputConnectionListener,
32 "org/qtproject/qt/android/QtInputConnection$QtInputConnectionListener")
33Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtWindowInterface")
34Q_DECLARE_JNI_CLASS(QtWindowInsetsController, "org/qtproject/qt/android/QtWindowInsetsController")
35
36QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
37 : QPlatformWindow(window), m_nativeQtWindow(nullptr),
38 m_surfaceContainerType(SurfaceContainer::TextureView), m_nativeParentQtWindow(nullptr)
39{
40 if (window->surfaceType() == QSurface::RasterSurface)
41 window->setSurfaceType(QSurface::OpenGLSurface);
42}
43
45{
47 return;
48
49 QWindow *window = QPlatformWindow::window();
50
51 if (parent()) {
52 QAndroidPlatformWindow *androidParent = static_cast<QAndroidPlatformWindow*>(parent());
53 if (!androidParent->isEmbeddingContainer())
54 m_nativeParentQtWindow = androidParent->nativeWindow();
55 }
56
58 QtJniTypes::QtInputConnectionListener listener =
59 reg->callInterface<QtJniTypes::QtInputInterface, QtJniTypes::QtInputConnectionListener>(
60 "getInputConnectionListener");
61
62 m_nativeQtWindow = QJniObject::construct<QtJniTypes::QtWindow>(
63 QNativeInterface::QAndroidApplication::context(),
64 isForeignWindow(), m_nativeParentQtWindow, listener);
65 m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId");
66
67 // the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save
68 // the fact that it's a raster window for now
69 m_isRaster = window->surfaceType() == QSurface::RasterSurface;
70
71 // the following is in relation to the virtual geometry
72 const bool forceMaximize = window->windowStates() & (Qt::WindowMaximized | Qt::WindowFullScreen);
73 const QRect nativeScreenGeometry = platformScreen()->availableGeometry();
74 if (forceMaximize) {
75 setGeometry(nativeScreenGeometry);
76 } else {
77 const QRect requestedNativeGeometry = QHighDpi::toNativePixels(window->geometry(), window);
78 const QRect availableDeviceIndependentGeometry = (window->parent())
79 ? window->parent()->geometry()
80 : QHighDpi::fromNativePixels(nativeScreenGeometry, window);
81 // initialGeometry returns in native pixels
82 const QRect finalNativeGeometry = QPlatformWindow::initialGeometry(
83 window, requestedNativeGeometry, availableDeviceIndependentGeometry.width(),
84 availableDeviceIndependentGeometry.height());
85 setGeometry(finalNativeGeometry);
86 }
87
88 if (window->isTopLevel())
89 platformScreen()->addWindow(this);
90
91 static bool ok = false;
92 static const int value = qEnvironmentVariableIntValue("QT_ANDROID_SURFACE_CONTAINER_TYPE", &ok);
93 if (ok) {
94 static const SurfaceContainer type = static_cast<SurfaceContainer>(value);
97 } else if (platformScreen()->windows().size() <= 1) {
98 // TODO should handle case where this changes at runtime -> need to change existing window
99 // into TextureView (or perhaps not, if the parent window would be SurfaceView, as long as
100 // onTop was false it would stay below the children)
102 }
103 qCDebug(lcQpaWindow) << "Window" << m_nativeViewId << "using surface container type"
104 << static_cast<int>(m_surfaceContainerType);
105}
106
108{
110 if (window()->isTopLevel())
111 platformScreen()->removeWindow(this);
112}
113
115{
116 if (m_nativeParentQtWindow.isValid()) {
117 m_nativeParentQtWindow.callMethod<void>("bringChildToBack", nativeViewId());
118 return;
119 }
120 platformScreen()->lower(this);
121}
122
124{
125 if (m_nativeParentQtWindow.isValid()) {
126 m_nativeParentQtWindow.callMethod<void>("bringChildToFront", nativeViewId());
127 return;
128 }
129 applySystemUi(window()->windowStates(), window()->flags());
130 platformScreen()->raise(this);
131}
132
134{
135 return m_safeAreaMargins;
136}
137
138void QAndroidPlatformWindow::setSafeAreaMargins(const QMargins safeMargins)
139{
140 m_safeAreaMargins = safeMargins;
141}
142
143void QAndroidPlatformWindow::setGeometry(const QRect &rect)
144{
145 QWindowSystemInterface::handleGeometryChange(window(), rect);
147 updateNativeWindow(window()->isVisible());
148}
149
151{
152 // The shell gives a safe area only to top-level windows and windows
153 // embedded in a native view. Qt derives it for any other window.
154 const QWindow *parent = window()->parent();
155 return parent && parent->handle()
156 && !static_cast<const QAndroidPlatformWindow *>(parent->handle())->isForeignWindow();
157}
158
160{
162 // Clipped child edges cannot add to the parent's unsafe area.
163 const auto *parent = static_cast<const QAndroidPlatformWindow *>(window()->parent()->handle());
164 const QMargins parentMargins = parent->safeAreaMargins();
165 const QSize parentSize = parent->geometry().size();
166 const QRect rect = geometry();
167 const QMargins margins(
168 qBound(0, parentMargins.left() - rect.x(), parentMargins.left()),
169 qBound(0, parentMargins.top() - rect.y(), parentMargins.top()),
170 qBound(0, parentMargins.right() - (parentSize.width() - rect.x() - rect.width()),
171 parentMargins.right()),
172 qBound(0, parentMargins.bottom() - (parentSize.height() - rect.y() - rect.height()),
173 parentMargins.bottom()));
174 if (margins != m_safeAreaMargins) {
175 m_safeAreaMargins = margins;
177 }
178 }
179 for (QObject *object : window()->children()) {
180 auto *child = qobject_cast<QWindow *>(object);
181 if (child && child->handle())
182 static_cast<QAndroidPlatformWindow *>(child->handle())->updateDerivedSafeAreaMargins();
183 }
184}
185
187{
189 return;
190 // A maximized or full screen window fills the root layout, so Android
191 // resizes it in the pass that rotates, before its Surface is redrawn.
192 const bool fillsLayout = window()->isTopLevel()
193 && window()->windowStates() & (Qt::WindowMaximized | Qt::WindowFullScreen);
194 const QRect rect = fillsLayout ? QRect() : geometry();
195 jint x = 0;
196 jint y = 0;
197 jint w = -1;
198 jint h = -1;
199 if (!rect.isNull()) {
200 x = rect.x();
201 y = rect.y();
202 w = rect.width();
203 h = rect.height();
204 }
205 m_nativeQtWindow.callMethod<void>("updateWindow", x, y, w, h, visible);
206}
207
209{
211 return;
212
213 if (window()->isTopLevel()) {
214 if (!visible && window() == qGuiApp->focusWindow()) {
215 platformScreen()->topVisibleWindowChanged();
216 } else {
217 const Qt::WindowStates states = window()->windowStates();
218 const Qt::WindowFlags flags = window()->flags();
219 applySystemUi(states, flags);
220 if (states & Qt::WindowFullScreen
221 || ((states & Qt::WindowMaximized) && (flags & Qt::ExpandedClientAreaHint)))
222 setGeometry(platformScreen()->geometry());
223 else if (states & Qt::WindowMaximized)
224 setGeometry(platformScreen()->availableGeometry());
226 }
227 }
228
229 if (visible && !isEmbeddingContainer()) {
230 const bool onTop = window()->flags() & Qt::WindowStaysOnTopHint;
231 const bool isOpaque = !format().hasAlpha()
232 && qFuzzyCompare(window()->opacity(), qreal(1.0));
233 m_nativeQtWindow.callMethod<void>("configureSurface", onTop, isOpaque,
234 m_surfaceContainerType);
235 }
237
238 if (geometry().isEmpty() || screen()->availableGeometry().isEmpty())
239 return;
240
241 QPlatformWindow::setVisible(visible);
243}
244
245void QAndroidPlatformWindow::setWindowState(Qt::WindowStates state)
246{
247 QPlatformWindow::setWindowState(state);
248
249 if (window()->isVisible())
250 applySystemUi(state, window()->flags());
251 // Whether the window fills the root layout depends on the state.
252 updateNativeWindow(window()->isVisible());
253}
254
255void QAndroidPlatformWindow::setWindowFlags(Qt::WindowFlags flags)
256{
257 QPlatformWindow::setWindowFlags(flags);
258
259 if (window()->isVisible())
260 applySystemUi(window()->windowStates(), flags);
261}
262
263void QAndroidPlatformWindow::setParent(const QPlatformWindow *window)
264{
265 using namespace QtJniTypes;
266
267 if (window) {
268 auto androidWindow = static_cast<const QAndroidPlatformWindow*>(window);
269 if (androidWindow->isEmbeddingContainer())
270 return;
271 // If we were a top level window, remove from screen
272 if (!m_nativeParentQtWindow.isValid())
273 platformScreen()->removeWindow(this);
274
275 const QtWindow parentWindow = androidWindow->nativeWindow();
276 // If this was a child window of another window, the java method takes care of that
277 m_nativeQtWindow.callMethod<void, QtWindow>("setParent", parentWindow.object());
278 m_nativeParentQtWindow = parentWindow;
279 } else if (QtAndroid::isQtApplication()) {
280 m_nativeQtWindow.callMethod<void, QtWindow>("setParent", nullptr);
281 m_nativeParentQtWindow = QJniObject();
282 platformScreen()->addWindow(this);
283 }
285}
286
288{
289 return m_nativeQtWindow.isValid() ? reinterpret_cast<WId>(m_nativeQtWindow.object()) : 0L;
290}
291
293{
294 return static_cast<QAndroidPlatformScreen *>(window()->screen()->handle());
295}
296
298{
299 //shut up warning from default implementation
300}
301
303{
305 return;
306 raise();
307 // For child windows, raise() only updates "Z-order", so focus must be requested explicitly.
308 if (m_nativeParentQtWindow.isValid())
309 QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason);
310}
311
312namespace {
313// The extended system UI modes Qt can take ownership of. Plain "normal" is not
314// a mode Qt owns, it is the absence of ownership, represented by an empty
315// optional, so window state the app manages itself is never touched by Qt.
316enum class SystemUiMode { Expanded, FullScreen };
317
318std::optional<SystemUiMode> systemUiModeFor(Qt::WindowStates states, Qt::WindowFlags flags)
319{
320 if (states & Qt::WindowFullScreen)
321 return SystemUiMode::FullScreen;
322 if (flags & Qt::ExpandedClientAreaHint)
323 return SystemUiMode::Expanded;
324 return std::nullopt;
325}
326
327// The mode Qt currently owns for the activity window, only ever touched on the
328// gui thread. Empty means Qt owns nothing and leaves the system UI to the app.
329std::optional<SystemUiMode> g_ownedSystemUiMode;
330
331void applySystemUiMode(std::optional<SystemUiMode> mode)
332{
333 g_ownedSystemUiMode = mode;
334
335 auto iface = qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>();
336 iface->runOnAndroidMainThread([mode, iface]() {
337 using namespace QtJniTypes;
338 auto activity = iface->context().object<Activity>();
339 if (mode == SystemUiMode::FullScreen)
340 QtWindowInsetsController::callStaticMethod("showFullScreen", activity);
341 else if (mode == SystemUiMode::Expanded)
342 QtWindowInsetsController::callStaticMethod("showExpanded", activity);
343 else
344 QtWindowInsetsController::callStaticMethod("showNormal", activity);
345 });
346}
347} // unnamed namespace
348
349void QAndroidPlatformWindow::applySystemUi(Qt::WindowStates states, Qt::WindowFlags flags)
350{
351 const bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window;
352 if (isNonRegularWindow)
353 return;
354
355 const std::optional<SystemUiMode> mode = systemUiModeFor(states, flags);
356 if (mode != g_ownedSystemUiMode)
357 applySystemUiMode(mode);
358}
359
360void QAndroidPlatformWindow::applySystemUiNative(JNIEnv *env, jobject object)
361{
362 Q_UNUSED(env)
363 Q_UNUSED(object)
364
365 if (!qGuiApp)
366 return;
367
368 // Some lifecycle events reset window state on the OS side. Re-assert the
369 // mode Qt owns, if any. When Qt owns nothing the app keeps its own state.
370 QMetaObject::invokeMethod(qGuiApp, [] {
371 if (g_ownedSystemUiMode)
372 applySystemUiMode(g_ownedSystemUiMode);
373 });
374}
375
377{
378 m_nativeQtWindow.callMethod<void>("updateFocusedEditText");
379}
380
382{
383 // Not exposed until Android provides a Surface, so QRhi never starts
384 // without a GPU surface. A child is on screen only while its parent is.
385 const QWindow *parent = window()->parent();
386 const auto state = qApp->applicationState();
387 return m_surfaceValid
388 && (!parent || !parent->handle() || parent->handle()->isExposed())
389 && (state == Qt::ApplicationInactive || state == Qt::ApplicationActive)
390 && window()->isVisible()
391 && !window()->geometry().isEmpty();
392}
393
395{
396 QRegion region;
397 if (isExposed())
398 region = QRect(QPoint(), geometry().size());
399
400 QWindowSystemInterface::handleExposeEvent(window(), region);
402 QWindowSystemInterface::flushWindowSystemEvents();
403}
404
406{
407 // Child exposure depends on this window's, so it changes with it.
408 for (QObject *object : window()->children()) {
409 auto *child = qobject_cast<QWindow *>(object);
410 if (!child || !child->handle())
411 continue;
412 auto *platformChild = static_cast<QAndroidPlatformWindow *>(child->handle());
413 QRegion region;
414 if (platformChild->isExposed())
415 region = QRect(QPoint(), platformChild->geometry().size());
416 QWindowSystemInterface::handleExposeEvent(child, region);
417 platformChild->sendChildExposeEvents();
418 }
419}
420
422{
423 if (m_nativeWindow || QThread::currentThread() != qApp->thread())
424 return;
425 if (!m_nativeQtWindow.isValid())
426 return;
427 // Ask Java for the Surface directly instead of waiting for the queued
428 // surfaceCreated, which would re-enter user code from inside makeCurrent().
429 auto surface = m_nativeQtWindow.callMethod<QtJniTypes::Surface>("getSurface");
430 if (surface.isValid() && surface.callMethod<jboolean>("isValid")) {
431 if (ANativeWindow *native = ANativeWindow_fromSurface(
432 QJniEnvironment::getJniEnv(), surface.object())) {
433 m_nativeWindow = native;
434 }
435 }
436}
437
438void QAndroidPlatformWindow::onSurfaceCreated(QtJniTypes::Surface surface)
439{
440 qCDebug(lcQpaWindow) << "onSurfaceCreated() for windowId" << m_nativeViewId;
441 if (isForeignWindow() || isEmbeddingContainer() || !surface.isValid())
442 return;
443
444 ANativeWindow *native = ANativeWindow_fromSurface(QJniEnvironment::getJniEnv(),
445 surface.object());
446 if (!native) {
447 qCWarning(lcQpaWindow) << "ANativeWindow_fromSurface returned null for windowId"
449 return;
450 }
451
452 if (native == m_nativeWindow) {
453 // Already fetched by ensureNativeWindow() or re-sent by
454 // Android, so keep the GPU surface and drop the extra reference.
455 ANativeWindow_release(native);
456 } else {
457 if (m_nativeWindow) {
458 // New Surface without a surfaceDestroyed. Release the old GPU
459 // surface first, else eglCreateWindowSurface() hits EGL_BAD_ALLOC.
460 QPlatformSurfaceEvent surfaceEvent(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
461 QGuiApplication::sendEvent(window(), &surfaceEvent);
463 }
464 m_nativeWindow = native;
465
466 // Pairs with SurfaceAboutToBeDestroyed. QWidget recreates its native
467 // state (winId) only on SurfaceCreated.
468 QPlatformSurfaceEvent surfaceEvent(QPlatformSurfaceEvent::SurfaceCreated);
469 QGuiApplication::sendEvent(window(), &surfaceEvent);
470 }
471
472 m_surfaceValid = true;
473
474 // Without a non-empty expose the QWindow never becomes exposed and
475 // renders nothing.
476 QWindowSystemInterface::handleExposeEvent(window(),
477 QRegion(QRect(QPoint(), geometry().size())));
479}
480
482{
483 qCDebug(lcQpaWindow) << "onRedrawNeeded() for windowId" << m_nativeViewId;
484 // Synchronous, so the frame for the current size is done when this returns.
485 if (isExposed()) {
486 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(
487 window(), QRegion(QRect(QPoint(), geometry().size())));
488 }
489}
490
492{
493 qCDebug(lcQpaWindow) << "onSurfaceDestroyed() for windowId" << m_nativeViewId;
494 if (isForeignWindow() || isEmbeddingContainer())
495 return;
496
497 // Unexpose first so the scene graph stops scheduling frames and doesn't
498 // rebuild its swapchain against the missing surface.
499 m_surfaceValid = false;
500 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(
501 window(), QRegion());
503
504 // Returns once the scene graph released its swapchain, so the render thread
505 // no longer uses the GPU surface.
506 QPlatformSurfaceEvent surfaceEvent(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
507 QGuiApplication::sendEvent(window(), &surfaceEvent);
508
510}
511
513{
515 if (m_nativeWindow) {
516 ANativeWindow_release(m_nativeWindow);
517 m_nativeWindow = nullptr;
518 }
519}
520
522{
524 // Embedding containers never construct the Java window, so there is
525 // nothing to destroy.
526 if (m_nativeQtWindow.isValid())
527 m_nativeQtWindow.callMethod<void>("destroySurface");
528}
529
531{
532 QWindow *modalWindow = QGuiApplication::modalWindow();
533 return modalWindow && modalWindow != window();
534}
535
537{
538 // Returns true if the window is a wrapper for a foreign window solely to allow embedding Qt
539 // into a native Android app, in which case we should not try to control it more than we "need" to
540 return !QtAndroid::isQtApplication() && window()->isTopLevel();
541}
542
543QAndroidPlatformWindow *QAndroidPlatformWindow::findWindowById(jint windowId)
544{
545 const QList<QWindow*> windows = qGuiApp->allWindows();
546 for (QWindow *w : windows) {
547 if (!w->handle())
548 continue;
549 auto *pw = static_cast<QAndroidPlatformWindow *>(w->handle());
550 if (pw->nativeViewId() == windowId)
551 return pw;
552 }
553 return nullptr;
554}
555
556void QAndroidPlatformWindow::surfaceCreated(JNIEnv *env, jobject object, jint windowId,
557 QtJniTypes::Surface surface)
558{
559 Q_UNUSED(env)
560 Q_UNUSED(object)
561 if (!qGuiApp)
562 return;
563 // Queued, not blocking, so the Android UI thread keeps compositing.
564 // Blocking here showed black bands flickering during transitions.
565 QJniObject keepalive(surface);
566 QMetaObject::invokeMethod(qApp, [windowId, keepalive]() {
567 if (auto *pw = findWindowById(windowId))
568 pw->onSurfaceCreated(QtJniTypes::Surface(keepalive));
569 }, Qt::QueuedConnection);
570}
571
572void QAndroidPlatformWindow::surfaceRedrawNeeded(JNIEnv *env, jobject object, jint windowId,
573 QtJniTypes::Runnable drawingFinished)
574{
575 Q_UNUSED(env)
576 Q_UNUSED(object)
577 QJniObject finished(drawingFinished);
578 // A parked Qt thread can't draw, and Android must not wait for it.
579 if (!qGuiApp || (QtAndroid::blockEventLoopsWhenSuspended()
580 && QAndroidEventDispatcherStopper::stopped())) {
581 finished.callMethod<void>("run");
582 return;
583 }
584 // Android keeps the old frame until drawingFinished runs, which may happen
585 // on any thread, so no wait here.
586 QMetaObject::invokeMethod(qApp, [windowId, finished]() {
587 if (auto *pw = findWindowById(windowId))
588 pw->onRedrawNeeded();
589 finished.callMethod<void>("run");
590 }, Qt::QueuedConnection);
591}
592
593void QAndroidPlatformWindow::surfaceDestroyed(JNIEnv *env, jobject object, jint windowId,
594 QtJniTypes::Runnable surfaceReleased)
595{
596 Q_UNUSED(env)
597 Q_UNUSED(object)
598 QJniObject released(surfaceReleased);
599 const auto finish = [released] {
600 if (released.isValid())
601 released.callMethod<void>("run");
602 };
603 if (!qGuiApp) {
604 finish();
605 return;
606 }
607 // Keep the producer alive until GUI-thread teardown, including after resume.
608 QMetaObject::invokeMethod(qApp, [windowId, finish]() {
609 if (auto *pw = findWindowById(windowId))
610 pw->onSurfaceDestroyed();
611 finish();
612 }, Qt::QueuedConnection);
613}
614
615void QAndroidPlatformWindow::windowFocusChanged(JNIEnv *env, jobject object,
616 jboolean focus, jint windowId)
617{
618 Q_UNUSED(env)
619 Q_UNUSED(object)
620 if (!qGuiApp)
621 return;
622 // Queued like the surface handlers, so focus never arrives before the
623 // surface state it depends on. Handling it inline broke QQuickWidget focus.
624 QMetaObject::invokeMethod(qApp, [windowId, focus]() {
625 QWindow *window = QtAndroid::windowFromId(windowId);
626 if (!window) {
627 qCWarning(lcQpaWindow,
628 "windowFocusChanged event received for non-existing window %d", windowId);
629 return;
630 }
631 if (focus)
632 QWindowSystemInterface::handleFocusWindowChanged(window);
633 else if (window == qGuiApp->focusWindow())
634 QWindowSystemInterface::handleFocusWindowChanged(nullptr);
635 }, Qt::QueuedConnection);
636}
637
638void QAndroidPlatformWindow::safeAreaMarginsChanged(JNIEnv *env, jobject object,
639 QtJniTypes::Insets insets, jint id)
640{
641 Q_UNUSED(env)
642 Q_UNUSED(object)
643
644 if (!qGuiApp)
645 return;
646
647 if (!insets.isValid())
648 return;
649
650 const QMargins safeMargins(insets.getField<int>("left"),
651 insets.getField<int>("top"),
652 insets.getField<int>("right"),
653 insets.getField<int>("bottom"));
654
655 // Queued so margins stay ordered with the geometry events they belong to.
656 QMetaObject::invokeMethod(qApp, [id, safeMargins]() {
657 QAndroidPlatformWindow *pw = findWindowById(id);
658 // A child of a Qt window derives its margins, the layout can be stale.
659 if (!pw || pw->isChildOfQtWindow())
660 return;
661 if (safeMargins != pw->safeAreaMargins()) {
662 pw->setSafeAreaMargins(safeMargins);
663 QWindowSystemInterface::handleSafeAreaMarginsChanged(pw->window());
664 }
665 pw->updateDerivedSafeAreaMargins();
666 }, Qt::QueuedConnection);
667}
668
669static void updateWindows(JNIEnv *env, jobject object)
670{
671 Q_UNUSED(env)
672 Q_UNUSED(object)
673
674 if (QGuiApplication::instance() != nullptr) {
675 const auto tlw = QGuiApplication::topLevelWindows();
676 for (QWindow *w : tlw) {
677
678 // Skip non-platform windows, e.g., offscreen windows.
679 if (!w->handle())
680 continue;
681
682 const QRect availableGeometry = w->screen()->availableGeometry();
683 const QRect geometry = w->geometry();
684 const bool isPositiveGeometry = (geometry.width() > 0 && geometry.height() > 0);
685 const bool isPositiveAvailableGeometry =
686 (availableGeometry.width() > 0 && availableGeometry.height() > 0);
687
688 if (isPositiveGeometry && isPositiveAvailableGeometry) {
689 const QRegion region = QRegion(QRect(QPoint(), w->geometry().size()));
690 QWindowSystemInterface::handleExposeEvent(w, region);
691 }
692 }
693 }
694}
695Q_DECLARE_JNI_NATIVE_METHOD(updateWindows)
696
697bool QAndroidPlatformWindow::registerNatives(QJniEnvironment &env)
698{
699 if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtWindow>::className(),
700 {
701 Q_JNI_NATIVE_METHOD(updateWindows),
702 Q_JNI_NATIVE_SCOPED_METHOD(surfaceCreated, QAndroidPlatformWindow),
703 Q_JNI_NATIVE_SCOPED_METHOD(surfaceRedrawNeeded, QAndroidPlatformWindow),
704 Q_JNI_NATIVE_SCOPED_METHOD(surfaceDestroyed, QAndroidPlatformWindow),
705 Q_JNI_NATIVE_SCOPED_METHOD(windowFocusChanged, QAndroidPlatformWindow),
706 Q_JNI_NATIVE_SCOPED_METHOD(safeAreaMarginsChanged, QAndroidPlatformWindow)
707 })) {
708 qCCritical(lcQpaWindow) << "RegisterNatives failed for"
709 << QtJniTypes::Traits<QtJniTypes::QtWindow>::className();
710 return false;
711 }
712
713 if (!env.registerNativeMethods(
714 QtJniTypes::Traits<QtJniTypes::QtWindowInsetsController>::className(),
715 {
716 Q_JNI_NATIVE_SCOPED_METHOD(applySystemUiNative, QAndroidPlatformWindow)
717 })) {
718 qCCritical(lcQpaWindow) << "RegisterNatives failed for"
719 << QtJniTypes::Traits<QtJniTypes::QtWindowInsetsController>::className();
720 return false;
721 }
722 return true;
723}
724
725QT_END_NAMESPACE
void propagateSizeHints() override
Reimplement to propagate the size hints of the QWindow.
virtual void applicationStateChanged(Qt::ApplicationState)
void setSafeAreaMargins(const QMargins safeMargins)
void setWindowFlags(Qt::WindowFlags flags) override
Requests setting the window flags of this surface to flags.
void setWindowState(Qt::WindowStates state) override
Requests setting the window state of this surface to type.
bool isExposed() const override
Returns if this window is exposed in the windowing system.
SurfaceContainer m_surfaceContainerType
QAndroidPlatformScreen * platformScreen() const
void requestActivateWindow() override
Reimplement to let Qt be able to request activation/focus for a window.
void applySystemUi(Qt::WindowStates states, Qt::WindowFlags flags)
void updateNativeWindow(bool visible)
void initialize() override
Called as part of QWindow::create(), after constructing the window.
void setVisible(bool visible) override
Reimplemented in subclasses to show the surface if visible is true, and hide it if visible is false.
void setParent(const QPlatformWindow *window) override
This function is called to enable native child window in QPA.
void lower() override
Reimplement to be able to let Qt lower windows to the bottom of the desktop.
void onSurfaceCreated(QtJniTypes::Surface surface)
WId winId() const override
Reimplement in subclasses to return a handle to the native window.
void raise() override
Reimplement to be able to let Qt raise windows to the top of the desktop.
void setGeometry(const QRect &rect) override
This function is called by Qt whenever a window is moved or resized using the QWindow API.
QMargins safeAreaMargins() const override
The safe area margins of a window represent the area that is safe to place content within,...
QObject * parent
Definition qobject.h:74
QRect window() const
Returns the window rectangle.
AndroidBackendRegister * backendRegister()
bool isQtApplication()
static void updateWindows(JNIEnv *env, jobject object)
#define qApp
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
#define qGuiApp