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