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
9
10#include "androidjnimain.h"
11
12#include <qguiapplication.h>
13#include <qpa/qwindowsysteminterface.h>
14#include <private/qhighdpiscaling_p.h>
15#include <private/qjnihelpers_p.h>
16
18
19Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window")
20
21Q_DECLARE_JNI_CLASS(QtWindowInterface, "org/qtproject/qt/android/QtWindowInterface")
22Q_DECLARE_JNI_CLASS(QtInputInterface, "org/qtproject/qt/android/QtInputInterface")
23Q_DECLARE_JNI_CLASS(QtInputConnectionListener,
24 "org/qtproject/qt/android/QtInputConnection$QtInputConnectionListener")
25Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtWindowInterface")
26Q_DECLARE_JNI_CLASS(QtWindowInsetsController, "org/qtproject/qt/android/QtWindowInsetsController")
27
28QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
29 : QPlatformWindow(window), m_nativeQtWindow(nullptr),
30 m_surfaceContainerType(SurfaceContainer::TextureView), m_nativeParentQtWindow(nullptr),
31 m_androidSurfaceObject(nullptr)
32{
33 if (window->surfaceType() == QSurface::RasterSurface)
34 window->setSurfaceType(QSurface::OpenGLSurface);
35}
36
38{
40 return;
41
42 QWindow *window = QPlatformWindow::window();
43
44 if (parent()) {
45 QAndroidPlatformWindow *androidParent = static_cast<QAndroidPlatformWindow*>(parent());
46 if (!androidParent->isEmbeddingContainer())
47 m_nativeParentQtWindow = androidParent->nativeWindow();
48 }
49
51 QtJniTypes::QtInputConnectionListener listener =
52 reg->callInterface<QtJniTypes::QtInputInterface, QtJniTypes::QtInputConnectionListener>(
53 "getInputConnectionListener");
54
55 m_nativeQtWindow = QJniObject::construct<QtJniTypes::QtWindow>(
56 QNativeInterface::QAndroidApplication::context(),
57 isForeignWindow(), m_nativeParentQtWindow, listener);
58 m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId");
59
60 // the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save
61 // the fact that it's a raster window for now
62 m_isRaster = window->surfaceType() == QSurface::RasterSurface;
63
64 // the following is in relation to the virtual geometry
65 const bool forceMaximize = window->windowStates() & (Qt::WindowMaximized | Qt::WindowFullScreen);
66 const QRect nativeScreenGeometry = platformScreen()->availableGeometry();
67 if (forceMaximize) {
68 setGeometry(nativeScreenGeometry);
69 } else {
70 const QRect requestedNativeGeometry = QHighDpi::toNativePixels(window->geometry(), window);
71 const QRect availableDeviceIndependentGeometry = (window->parent())
72 ? window->parent()->geometry()
73 : QHighDpi::fromNativePixels(nativeScreenGeometry, window);
74 // initialGeometry returns in native pixels
75 const QRect finalNativeGeometry = QPlatformWindow::initialGeometry(
76 window, requestedNativeGeometry, availableDeviceIndependentGeometry.width(),
77 availableDeviceIndependentGeometry.height());
78 setGeometry(finalNativeGeometry);
79 }
80
81 if (window->isTopLevel())
82 platformScreen()->addWindow(this);
83
84 static bool ok = false;
85 static const int value = qEnvironmentVariableIntValue("QT_ANDROID_SURFACE_CONTAINER_TYPE", &ok);
86 if (ok) {
87 static const SurfaceContainer type = static_cast<SurfaceContainer>(value);
90 } else if (platformScreen()->windows().size() <= 1) {
91 // TODO should handle case where this changes at runtime -> need to change existing window
92 // into TextureView (or perhaps not, if the parent window would be SurfaceView, as long as
93 // onTop was false it would stay below the children)
95 }
96 qCDebug(lcQpaWindow) << "Window" << m_nativeViewId << "using surface container type"
97 << static_cast<int>(m_surfaceContainerType);
98}
99
101{
102 const auto guard = destructionGuard();
103 if (window()->isTopLevel())
104 platformScreen()->removeWindow(this);
105}
106
107
109{
110 if (m_nativeParentQtWindow.isValid()) {
111 m_nativeParentQtWindow.callMethod<void>("bringChildToBack", nativeViewId());
112 return;
113 }
114 platformScreen()->lower(this);
115}
116
118{
119 if (m_nativeParentQtWindow.isValid()) {
120 m_nativeParentQtWindow.callMethod<void>("bringChildToFront", nativeViewId());
121 QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason);
122 return;
123 }
124 updateSystemUiVisibility(window()->windowStates(), window()->flags());
125 platformScreen()->raise(this);
126}
127
129{
130 return m_safeAreaMargins;
131}
132
133void QAndroidPlatformWindow::setSafeAreaMargins(const QMargins safeMargins)
134{
135 m_safeAreaMargins = safeMargins;
136}
137
138void QAndroidPlatformWindow::setGeometry(const QRect &rect)
139{
141 Q_ASSERT(m_nativeQtWindow.isValid());
142
143 jint x = 0;
144 jint y = 0;
145 jint w = -1;
146 jint h = -1;
147 if (!rect.isNull()) {
148 x = rect.x();
149 y = rect.y();
150 w = rect.width();
151 h = rect.height();
152 }
153 m_nativeQtWindow.callMethod<void>("setGeometry", x, y, w, h);
154 }
155
156 QWindowSystemInterface::handleGeometryChange(window(), rect);
157}
158
160{
162 return;
163
164 if (window()->isTopLevel()) {
165 if (!visible && window() == qGuiApp->focusWindow()) {
166 platformScreen()->topVisibleWindowChanged();
167 } else {
168 const Qt::WindowStates states = window()->windowStates();
169 const Qt::WindowFlags flags = window()->flags();
170 updateSystemUiVisibility(states, flags);
171 if (states & Qt::WindowFullScreen || flags & Qt::ExpandedClientAreaHint)
172 setGeometry(platformScreen()->geometry());
173 else if (states & Qt::WindowMaximized)
174 setGeometry(platformScreen()->availableGeometry());
176 }
177 }
178
179 m_nativeQtWindow.callMethod<void>("setVisible", visible);
180
181 if (geometry().isEmpty() || screen()->availableGeometry().isEmpty())
182 return;
183
184 QPlatformWindow::setVisible(visible);
185}
186
187void QAndroidPlatformWindow::setWindowState(Qt::WindowStates state)
188{
189 QPlatformWindow::setWindowState(state);
190
191 if (window()->isVisible())
192 updateSystemUiVisibility(state, window()->flags());
193}
194
195void QAndroidPlatformWindow::setWindowFlags(Qt::WindowFlags flags)
196{
197 QPlatformWindow::setWindowFlags(flags);
198
199 if (window()->isVisible())
200 updateSystemUiVisibility(window()->windowStates(), flags);
201}
202
203void QAndroidPlatformWindow::setParent(const QPlatformWindow *window)
204{
205 using namespace QtJniTypes;
206
207 if (window) {
208 auto androidWindow = static_cast<const QAndroidPlatformWindow*>(window);
209 if (androidWindow->isEmbeddingContainer())
210 return;
211 // If we were a top level window, remove from screen
212 if (!m_nativeParentQtWindow.isValid())
213 platformScreen()->removeWindow(this);
214
215 const QtWindow parentWindow = androidWindow->nativeWindow();
216 // If this was a child window of another window, the java method takes care of that
217 m_nativeQtWindow.callMethod<void, QtWindow>("setParent", parentWindow.object());
218 m_nativeParentQtWindow = parentWindow;
219 } else if (QtAndroid::isQtApplication()) {
220 m_nativeQtWindow.callMethod<void, QtWindow>("setParent", nullptr);
221 m_nativeParentQtWindow = QJniObject();
222 platformScreen()->addWindow(this);
223 }
224}
225
227{
228 return m_nativeQtWindow.isValid() ? reinterpret_cast<WId>(m_nativeQtWindow.object()) : 0L;
229}
230
232{
233 return static_cast<QAndroidPlatformScreen *>(window()->screen()->handle());
234}
235
237{
238 //shut up warning from default implementation
239}
240
242{
243 // raise() will handle differences between top level and child windows, and requesting focus
244 if (!blockedByModal())
245 raise();
246}
247
248void QAndroidPlatformWindow::updateSystemUiVisibility(Qt::WindowStates states, Qt::WindowFlags flags)
249{
250 const bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window;
251 if (!isNonRegularWindow) {
252 auto iface = qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>();
253 iface->runOnAndroidMainThread([=]() {
254 using namespace QtJniTypes;
255 auto activity = iface->context().object<Activity>();
256 if (states & Qt::WindowFullScreen)
257 QtWindowInsetsController::callStaticMethod("showFullScreen", activity);
258 else if (flags & Qt::ExpandedClientAreaHint)
259 QtWindowInsetsController::callStaticMethod("showExpanded", activity);
260 else
261 QtWindowInsetsController::callStaticMethod("showNormal", activity);
262 });
263 }
264}
265
267{
268 m_nativeQtWindow.callMethod<void>("updateFocusedEditText");
269}
270
272{
273 return qApp->applicationState() > Qt::ApplicationHidden
274 && window()->isVisible()
275 && !window()->geometry().isEmpty();
276}
277
279{
280 QRegion region;
281 if (isExposed())
282 region = QRect(QPoint(), geometry().size());
283
284 QWindowSystemInterface::handleExposeEvent(window(), region);
285 QWindowSystemInterface::flushWindowSystemEvents();
286}
287
289{
290 const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
291 const bool isOpaque = !format().hasAlpha() && qFuzzyCompare(window()->opacity(), qreal(1.0));
292
293 m_nativeQtWindow.callMethod<void>("createSurface", windowStaysOnTop, 32, isOpaque,
294 m_surfaceContainerType);
296}
297
299{
301 m_nativeQtWindow.callMethod<void>("destroySurface");
303 }
304}
305
306void QAndroidPlatformWindow::onSurfaceChanged(QtJniTypes::Surface surface)
307{
309 const bool surfaceIsValid = surface.isValid();
310 qCDebug(lcQpaWindow) << "onSurfaceChanged(): valid Surface received" << surfaceIsValid;
311 m_androidSurfaceObject = surface;
312 if (surfaceIsValid) {
313 // wait until we have a valid surface to draw into
314 m_surfaceWaitCondition.wakeOne();
315 } else {
317 }
318
320
322}
323
325{
326 QRect availableGeometry = screen()->availableGeometry();
327 if (!geometry().isNull() && !availableGeometry.isNull()) {
328 QWindowSystemInterface::handleExposeEvent(window(),
329 QRegion(QRect(QPoint(), geometry().size())));
330 }
331}
332
334{
335 QWindow *modalWindow = QGuiApplication::modalWindow();
336 return modalWindow && modalWindow != window();
337}
338
340{
341 // Returns true if the window is a wrapper for a foreign window solely to allow embedding Qt
342 // into a native Android app, in which case we should not try to control it more than we "need" to
343 return !QtAndroid::isQtApplication() && window()->isTopLevel();
344}
345
346void QAndroidPlatformWindow::setSurface(JNIEnv *env, jobject object, jint windowId,
347 QtJniTypes::Surface surface)
348{
349 Q_UNUSED(env)
350 Q_UNUSED(object)
351
352 if (!qGuiApp)
353 return;
354
355 const QList<QWindow*> windows = qGuiApp->allWindows();
356 for (QWindow * window : windows) {
357 if (!window->handle())
358 continue;
359 QAndroidPlatformWindow *platformWindow =
360 static_cast<QAndroidPlatformWindow *>(window->handle());
361 const auto guard = platformWindow->destructionGuard();
362 if (!platformWindow->m_androidSurfaceCreated)
363 continue;
364 if (platformWindow->nativeViewId() == windowId)
365 platformWindow->onSurfaceChanged(surface);
366 }
367}
368
369void QAndroidPlatformWindow::windowFocusChanged(JNIEnv *env, jobject object,
370 jboolean focus, jint windowId)
371{
372 Q_UNUSED(env)
373 Q_UNUSED(object)
374 QWindow* window = QtAndroid::windowFromId(windowId);
375 if (!window) {
376 qCWarning(lcQpaWindow,
377 "windowFocusChanged event received for non-existing window %d", windowId);
378 return;
379 }
380
381 if (focus) {
382 QWindowSystemInterface::handleFocusWindowChanged(window);
383 } else if (!focus && window == qGuiApp->focusWindow()) {
384 // Clear focus if current window has lost focus
385 QWindowSystemInterface::handleFocusWindowChanged(nullptr);
386 }
387}
388
389void QAndroidPlatformWindow::safeAreaMarginsChanged(JNIEnv *env, jobject object,
390 QtJniTypes::Insets insets, jint id)
391{
392 Q_UNUSED(env)
393 Q_UNUSED(object)
394
395 if (!qGuiApp)
396 return;
397
398 if (!insets.isValid())
399 return;
400
401 QAndroidPlatformWindow *pWindow = nullptr;
402 for (QWindow *window : qGuiApp->allWindows()) {
403 if (!window->handle())
404 continue;
405 QAndroidPlatformWindow *pw = static_cast<QAndroidPlatformWindow *>(window->handle());
406 if (pw->nativeViewId() == id) {
407 pWindow = pw;
408 break;
409 }
410 }
411
412 if (!pWindow)
413 return;
414
415 QMargins safeMargins = QMargins(
416 insets.getField<int>("left"),
417 insets.getField<int>("top"),
418 insets.getField<int>("right"),
419 insets.getField<int>("bottom"));
420
421 if (safeMargins != pWindow->safeAreaMargins()) {
422 pWindow->setSafeAreaMargins(safeMargins);
423 QWindowSystemInterface::handleSafeAreaMarginsChanged(pWindow->window());
424 }
425}
426
427static void updateWindows(JNIEnv *env, jobject object)
428{
429 Q_UNUSED(env)
430 Q_UNUSED(object)
431
432 if (QGuiApplication::instance() != nullptr) {
433 const auto tlw = QGuiApplication::topLevelWindows();
434 for (QWindow *w : tlw) {
435
436 // Skip non-platform windows, e.g., offscreen windows.
437 if (!w->handle())
438 continue;
439
440 const QRect availableGeometry = w->screen()->availableGeometry();
441 const QRect geometry = w->geometry();
442 const bool isPositiveGeometry = (geometry.width() > 0 && geometry.height() > 0);
443 const bool isPositiveAvailableGeometry =
444 (availableGeometry.width() > 0 && availableGeometry.height() > 0);
445
446 if (isPositiveGeometry && isPositiveAvailableGeometry) {
447 const QRegion region = QRegion(QRect(QPoint(), w->geometry().size()));
448 QWindowSystemInterface::handleExposeEvent(w, region);
449 }
450 }
451 }
452}
453Q_DECLARE_JNI_NATIVE_METHOD(updateWindows)
454
455/*
456 Due to calls originating from Android, it is possible for native methods to
457 try to manipulate any given instance of QAndroidPlatformWindow when it is
458 already being destroyed. So we use this to guard against that. It is called
459 in the destructor, and should also be called in any function registered to
460 be called from java that may touch an instance of QAndroidPlatformWindow.
461 */
462QMutexLocker<QMutex> QAndroidPlatformWindow::destructionGuard()
463{
464 return QMutexLocker(&m_destructionMutex);
465}
466
467Q_CONSTINIT static QBasicAtomicInt g_surfacesCounter = Q_BASIC_ATOMIC_INITIALIZER(0);
468
470{
471 return g_surfacesCounter.loadRelaxed();
472}
473
475{
476 g_surfacesCounter.fetchAndAddRelaxed(1);
477}
478
480{
481 int cur = g_surfacesCounter.loadRelaxed();
482 while (true) {
483 if (cur == 0)
484 return;
485
486 if (g_surfacesCounter.testAndSetRelaxed(cur, cur - 1))
487 return;
488 }
489}
490
491bool QAndroidPlatformWindow::registerNatives(QJniEnvironment &env)
492{
493 if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtWindow>::className(),
494 {
495 Q_JNI_NATIVE_METHOD(updateWindows),
496 Q_JNI_NATIVE_SCOPED_METHOD(setSurface, QAndroidPlatformWindow),
497 Q_JNI_NATIVE_SCOPED_METHOD(windowFocusChanged, QAndroidPlatformWindow),
498 Q_JNI_NATIVE_SCOPED_METHOD(safeAreaMarginsChanged, QAndroidPlatformWindow)
499 })) {
500 qCCritical(lcQpaWindow) << "RegisterNatives failed for"
501 << QtJniTypes::Traits<QtJniTypes::QtWindow>::className();
502 return false;
503 }
504 return true;
505}
506
507QT_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.
void onSurfaceChanged(QtJniTypes::Surface surface)
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 updateSystemUiVisibility(Qt::WindowStates states, Qt::WindowFlags flags)
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.
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:73
Combined button and popup list for selecting options.
AndroidBackendRegister * backendRegister()
QWindow * windowFromId(int windowId)
bool isQtApplication()
static void updateWindows(JNIEnv *env, jobject object)
#define qApp
#define qGuiApp