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 return;
122 }
123 updateSystemUiVisibility(window()->windowStates(), window()->flags());
124 platformScreen()->raise(this);
125}
126
128{
129 return m_safeAreaMargins;
130}
131
132void QAndroidPlatformWindow::setSafeAreaMargins(const QMargins safeMargins)
133{
134 m_safeAreaMargins = safeMargins;
135}
136
137void QAndroidPlatformWindow::setGeometry(const QRect &rect)
138{
140 Q_ASSERT(m_nativeQtWindow.isValid());
141
142 jint x = 0;
143 jint y = 0;
144 jint w = -1;
145 jint h = -1;
146 if (!rect.isNull()) {
147 x = rect.x();
148 y = rect.y();
149 w = rect.width();
150 h = rect.height();
151 }
152 m_nativeQtWindow.callMethod<void>("setGeometry", x, y, w, h);
153 }
154
155 QWindowSystemInterface::handleGeometryChange(window(), rect);
156}
157
159{
161 return;
162
163 if (window()->isTopLevel()) {
164 if (!visible && window() == qGuiApp->focusWindow()) {
165 platformScreen()->topVisibleWindowChanged();
166 } else {
167 const Qt::WindowStates states = window()->windowStates();
168 const Qt::WindowFlags flags = window()->flags();
169 updateSystemUiVisibility(states, flags);
170 if (states & Qt::WindowFullScreen || flags & Qt::ExpandedClientAreaHint)
171 setGeometry(platformScreen()->geometry());
172 else if (states & Qt::WindowMaximized)
173 setGeometry(platformScreen()->availableGeometry());
175 }
176 }
177
178 m_nativeQtWindow.callMethod<void>("setVisible", visible);
179
180 if (geometry().isEmpty() || screen()->availableGeometry().isEmpty())
181 return;
182
183 QPlatformWindow::setVisible(visible);
184}
185
186void QAndroidPlatformWindow::setWindowState(Qt::WindowStates state)
187{
188 QPlatformWindow::setWindowState(state);
189
190 if (window()->isVisible())
191 updateSystemUiVisibility(state, window()->flags());
192}
193
194void QAndroidPlatformWindow::setWindowFlags(Qt::WindowFlags flags)
195{
196 QPlatformWindow::setWindowFlags(flags);
197
198 if (window()->isVisible())
199 updateSystemUiVisibility(window()->windowStates(), flags);
200}
201
202void QAndroidPlatformWindow::setParent(const QPlatformWindow *window)
203{
204 using namespace QtJniTypes;
205
206 if (window) {
207 auto androidWindow = static_cast<const QAndroidPlatformWindow*>(window);
208 if (androidWindow->isEmbeddingContainer())
209 return;
210 // If we were a top level window, remove from screen
211 if (!m_nativeParentQtWindow.isValid())
212 platformScreen()->removeWindow(this);
213
214 const QtWindow parentWindow = androidWindow->nativeWindow();
215 // If this was a child window of another window, the java method takes care of that
216 m_nativeQtWindow.callMethod<void, QtWindow>("setParent", parentWindow.object());
217 m_nativeParentQtWindow = parentWindow;
218 } else if (QtAndroid::isQtApplication()) {
219 m_nativeQtWindow.callMethod<void, QtWindow>("setParent", nullptr);
220 m_nativeParentQtWindow = QJniObject();
221 platformScreen()->addWindow(this);
222 }
223}
224
226{
227 return m_nativeQtWindow.isValid() ? reinterpret_cast<WId>(m_nativeQtWindow.object()) : 0L;
228}
229
231{
232 return static_cast<QAndroidPlatformScreen *>(window()->screen()->handle());
233}
234
236{
237 //shut up warning from default implementation
238}
239
241{
243 return;
244 raise();
245 // For child windows, raise() only updates "Z-order", so focus must be requested explicitly.
246 if (m_nativeParentQtWindow.isValid())
247 QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason);
248}
249
250void QAndroidPlatformWindow::updateSystemUiVisibility(Qt::WindowStates states, Qt::WindowFlags flags)
251{
252 const bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window;
253 if (!isNonRegularWindow) {
254 auto iface = qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>();
255 iface->runOnAndroidMainThread([=]() {
256 using namespace QtJniTypes;
257 auto activity = iface->context().object<Activity>();
258 if (states & Qt::WindowFullScreen)
259 QtWindowInsetsController::callStaticMethod("showFullScreen", activity);
260 else if (flags & Qt::ExpandedClientAreaHint)
261 QtWindowInsetsController::callStaticMethod("showExpanded", activity);
262 else
263 QtWindowInsetsController::callStaticMethod("showNormal", activity);
264 });
265 }
266}
267
269{
270 m_nativeQtWindow.callMethod<void>("updateFocusedEditText");
271}
272
274{
275 return qApp->applicationState() > Qt::ApplicationHidden
276 && window()->isVisible()
277 && !window()->geometry().isEmpty();
278}
279
281{
282 QRegion region;
283 if (isExposed())
284 region = QRect(QPoint(), geometry().size());
285
286 QWindowSystemInterface::handleExposeEvent(window(), region);
287 QWindowSystemInterface::flushWindowSystemEvents();
288}
289
291{
292 const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
293 const bool isOpaque = !format().hasAlpha() && qFuzzyCompare(window()->opacity(), qreal(1.0));
294
295 m_nativeQtWindow.callMethod<void>("createSurface", windowStaysOnTop, 32, isOpaque,
296 m_surfaceContainerType);
298}
299
301{
303 m_nativeQtWindow.callMethod<void>("destroySurface");
305 }
306}
307
308void QAndroidPlatformWindow::onSurfaceChanged(QtJniTypes::Surface surface)
309{
311 const bool surfaceIsValid = surface.isValid();
312 qCDebug(lcQpaWindow) << "onSurfaceChanged(): valid Surface received" << surfaceIsValid;
313 m_androidSurfaceObject = surface;
314 if (surfaceIsValid) {
315 // wait until we have a valid surface to draw into
316 m_surfaceWaitCondition.wakeOne();
317 } else {
319 }
320
322
324}
325
327{
328 QRect availableGeometry = screen()->availableGeometry();
329 if (!geometry().isNull() && !availableGeometry.isNull()) {
330 QWindowSystemInterface::handleExposeEvent(window(),
331 QRegion(QRect(QPoint(), geometry().size())));
332 }
333}
334
336{
337 QWindow *modalWindow = QGuiApplication::modalWindow();
338 return modalWindow && modalWindow != window();
339}
340
342{
343 // Returns true if the window is a wrapper for a foreign window solely to allow embedding Qt
344 // into a native Android app, in which case we should not try to control it more than we "need" to
345 return !QtAndroid::isQtApplication() && window()->isTopLevel();
346}
347
348void QAndroidPlatformWindow::setSurface(JNIEnv *env, jobject object, jint windowId,
349 QtJniTypes::Surface surface)
350{
351 Q_UNUSED(env)
352 Q_UNUSED(object)
353
354 if (!qGuiApp)
355 return;
356
357 const QList<QWindow*> windows = qGuiApp->allWindows();
358 for (QWindow * window : windows) {
359 if (!window->handle())
360 continue;
361 QAndroidPlatformWindow *platformWindow =
362 static_cast<QAndroidPlatformWindow *>(window->handle());
363 const auto guard = platformWindow->destructionGuard();
364 if (!platformWindow->m_androidSurfaceCreated)
365 continue;
366 if (platformWindow->nativeViewId() == windowId)
367 platformWindow->onSurfaceChanged(surface);
368 }
369}
370
371void QAndroidPlatformWindow::windowFocusChanged(JNIEnv *env, jobject object,
372 jboolean focus, jint windowId)
373{
374 Q_UNUSED(env)
375 Q_UNUSED(object)
376 QWindow* window = QtAndroid::windowFromId(windowId);
377 if (!window) {
378 qCWarning(lcQpaWindow,
379 "windowFocusChanged event received for non-existing window %d", windowId);
380 return;
381 }
382
383 if (focus) {
384 QWindowSystemInterface::handleFocusWindowChanged(window);
385 } else if (!focus && window == qGuiApp->focusWindow()) {
386 // Clear focus if current window has lost focus
387 QWindowSystemInterface::handleFocusWindowChanged(nullptr);
388 }
389}
390
391void QAndroidPlatformWindow::safeAreaMarginsChanged(JNIEnv *env, jobject object,
392 QtJniTypes::Insets insets, jint id)
393{
394 Q_UNUSED(env)
395 Q_UNUSED(object)
396
397 if (!qGuiApp)
398 return;
399
400 if (!insets.isValid())
401 return;
402
403 QAndroidPlatformWindow *pWindow = nullptr;
404 for (QWindow *window : qGuiApp->allWindows()) {
405 if (!window->handle())
406 continue;
407 QAndroidPlatformWindow *pw = static_cast<QAndroidPlatformWindow *>(window->handle());
408 if (pw->nativeViewId() == id) {
409 pWindow = pw;
410 break;
411 }
412 }
413
414 if (!pWindow)
415 return;
416
417 QMargins safeMargins = QMargins(
418 insets.getField<int>("left"),
419 insets.getField<int>("top"),
420 insets.getField<int>("right"),
421 insets.getField<int>("bottom"));
422
423 if (safeMargins != pWindow->safeAreaMargins()) {
424 pWindow->setSafeAreaMargins(safeMargins);
425 QWindowSystemInterface::handleSafeAreaMarginsChanged(pWindow->window());
426 }
427}
428
429static void updateWindows(JNIEnv *env, jobject object)
430{
431 Q_UNUSED(env)
432 Q_UNUSED(object)
433
434 if (QGuiApplication::instance() != nullptr) {
435 const auto tlw = QGuiApplication::topLevelWindows();
436 for (QWindow *w : tlw) {
437
438 // Skip non-platform windows, e.g., offscreen windows.
439 if (!w->handle())
440 continue;
441
442 const QRect availableGeometry = w->screen()->availableGeometry();
443 const QRect geometry = w->geometry();
444 const bool isPositiveGeometry = (geometry.width() > 0 && geometry.height() > 0);
445 const bool isPositiveAvailableGeometry =
446 (availableGeometry.width() > 0 && availableGeometry.height() > 0);
447
448 if (isPositiveGeometry && isPositiveAvailableGeometry) {
449 const QRegion region = QRegion(QRect(QPoint(), w->geometry().size()));
450 QWindowSystemInterface::handleExposeEvent(w, region);
451 }
452 }
453 }
454}
455Q_DECLARE_JNI_NATIVE_METHOD(updateWindows)
456
457/*
458 Due to calls originating from Android, it is possible for native methods to
459 try to manipulate any given instance of QAndroidPlatformWindow when it is
460 already being destroyed. So we use this to guard against that. It is called
461 in the destructor, and should also be called in any function registered to
462 be called from java that may touch an instance of QAndroidPlatformWindow.
463 */
464QMutexLocker<QMutex> QAndroidPlatformWindow::destructionGuard()
465{
466 return QMutexLocker(&m_destructionMutex);
467}
468
469Q_CONSTINIT static QBasicAtomicInt g_surfacesCounter = Q_BASIC_ATOMIC_INITIALIZER(0);
470
472{
473 return g_surfacesCounter.loadRelaxed();
474}
475
477{
478 g_surfacesCounter.fetchAndAddRelaxed(1);
479}
480
482{
483 int cur = g_surfacesCounter.loadRelaxed();
484 while (true) {
485 if (cur == 0)
486 return;
487
488 if (g_surfacesCounter.testAndSetRelaxed(cur, cur - 1))
489 return;
490 }
491}
492
493bool QAndroidPlatformWindow::registerNatives(QJniEnvironment &env)
494{
495 if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtWindow>::className(),
496 {
497 Q_JNI_NATIVE_METHOD(updateWindows),
498 Q_JNI_NATIVE_SCOPED_METHOD(setSurface, QAndroidPlatformWindow),
499 Q_JNI_NATIVE_SCOPED_METHOD(windowFocusChanged, QAndroidPlatformWindow),
500 Q_JNI_NATIVE_SCOPED_METHOD(safeAreaMarginsChanged, QAndroidPlatformWindow)
501 })) {
502 qCCritical(lcQpaWindow) << "RegisterNatives failed for"
503 << QtJniTypes::Traits<QtJniTypes::QtWindow>::className();
504 return false;
505 }
506 return true;
507}
508
509QT_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:74
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