QGuiApplication on Android can now detect multiple displays
- Extending QtNative.java with access to DisplayManager and get details about available displays - Extending Android Platform Integration with display's list handling - Change QAndroidPlatformScreen to initialize itself from QJniObject representation of an android Display object - Move initialization of Primary display from QAndroidPlatformScreen to QAndroidPlatformIntegration Change-Id: I3d8f97f5cf9f81bbecc8716c25ff323097e57a15 Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
parent
c7b93d471d
commit
fbf586db2c
@ -8,6 +8,7 @@ import java.io.File;
|
|||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -37,6 +38,8 @@ import android.view.Menu;
|
|||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.hardware.display.DisplayManager;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
|
|
||||||
@ -592,6 +595,18 @@ public class QtNative
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<Display> getAvailableDisplays()
|
||||||
|
{
|
||||||
|
Context context = getContext();
|
||||||
|
DisplayManager displayManager =
|
||||||
|
(DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
|
||||||
|
if (displayManager != null) {
|
||||||
|
Display[] displays = displayManager.getDisplays();
|
||||||
|
return Arrays.asList(displays);
|
||||||
|
}
|
||||||
|
return new ArrayList<Display>();
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean startApplication(String params, String mainLib) throws Exception
|
public static boolean startApplication(String params, String mainLib) throws Exception
|
||||||
{
|
{
|
||||||
if (params == null)
|
if (params == null)
|
||||||
|
@ -56,6 +56,11 @@ Qt::ScreenOrientation QAndroidPlatformIntegration::m_nativeOrientation = Qt::Pri
|
|||||||
bool QAndroidPlatformIntegration::m_showPasswordEnabled = false;
|
bool QAndroidPlatformIntegration::m_showPasswordEnabled = false;
|
||||||
static bool m_running = false;
|
static bool m_running = false;
|
||||||
|
|
||||||
|
Q_DECLARE_JNI_CLASS(QtNative, "org/qtproject/qt/android/QtNative")
|
||||||
|
Q_DECLARE_JNI_CLASS(Display, "android/view/Display")
|
||||||
|
|
||||||
|
Q_DECLARE_JNI_TYPE(List, "Ljava/util/List;")
|
||||||
|
|
||||||
void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteArray &resource)
|
void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteArray &resource)
|
||||||
{
|
{
|
||||||
if (resource=="JavaVM")
|
if (resource=="JavaVM")
|
||||||
@ -163,10 +168,33 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶
|
|||||||
if (Q_UNLIKELY(!eglBindAPI(EGL_OPENGL_ES_API)))
|
if (Q_UNLIKELY(!eglBindAPI(EGL_OPENGL_ES_API)))
|
||||||
qFatal("Could not bind GL_ES API");
|
qFatal("Could not bind GL_ES API");
|
||||||
|
|
||||||
m_primaryScreen = new QAndroidPlatformScreen();
|
static const int primaryDisplayId = QJniObject::getStaticField<jint>(
|
||||||
QWindowSystemInterface::handleScreenAdded(m_primaryScreen);
|
QtJniTypes::className<QtJniTypes::Display>(), "DEFAULT_DISPLAY");
|
||||||
m_primaryScreen->setSizeParameters(m_defaultPhysicalSize, m_defaultScreenSize,
|
|
||||||
m_defaultAvailableGeometry);
|
const QJniObject nativeDisplaysList = QJniObject::callStaticObjectMethod<QtJniTypes::List>(
|
||||||
|
QtJniTypes::className<QtJniTypes::QtNative>(),
|
||||||
|
"getAvailableDisplays");
|
||||||
|
|
||||||
|
const int numberOfAvailableDisplays = nativeDisplaysList.callMethod<jint>("size");
|
||||||
|
for (int i = 0; i < numberOfAvailableDisplays; ++i) {
|
||||||
|
const QJniObject display =
|
||||||
|
nativeDisplaysList.callObjectMethod<jobject, jint>("get", jint(i));
|
||||||
|
|
||||||
|
const bool isPrimary = (primaryDisplayId == display.callMethod<jint>("getDisplayId"));
|
||||||
|
auto screen = new QAndroidPlatformScreen(display);
|
||||||
|
|
||||||
|
if (isPrimary)
|
||||||
|
m_primaryScreen = screen;
|
||||||
|
|
||||||
|
QWindowSystemInterface::handleScreenAdded(screen, isPrimary);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numberOfAvailableDisplays == 0) {
|
||||||
|
// If no displays are found, add a dummy display
|
||||||
|
auto defaultScreen = new QAndroidPlatformScreen(QJniObject {});
|
||||||
|
m_primaryScreen = defaultScreen;
|
||||||
|
QWindowSystemInterface::handleScreenAdded(defaultScreen, true);
|
||||||
|
}
|
||||||
|
|
||||||
m_mainThread = QThread::currentThread();
|
m_mainThread = QThread::currentThread();
|
||||||
|
|
||||||
|
@ -52,11 +52,17 @@ private:
|
|||||||
# define PROFILE_SCOPE
|
# define PROFILE_SCOPE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QAndroidPlatformScreen::QAndroidPlatformScreen()
|
Q_DECLARE_JNI_CLASS(Display, "android/view/Display")
|
||||||
|
|
||||||
|
Q_DECLARE_JNI_TYPE(DisplayMode, "Landroid/view/Display$Mode;")
|
||||||
|
|
||||||
|
QAndroidPlatformScreen::QAndroidPlatformScreen(const QJniObject &displayObject)
|
||||||
: QObject(), QPlatformScreen()
|
: QObject(), QPlatformScreen()
|
||||||
{
|
{
|
||||||
m_availableGeometry = QAndroidPlatformIntegration::m_defaultAvailableGeometry;
|
m_availableGeometry = QAndroidPlatformIntegration::m_defaultAvailableGeometry;
|
||||||
m_size = QAndroidPlatformIntegration::m_defaultScreenSize;
|
m_size = QAndroidPlatformIntegration::m_defaultScreenSize;
|
||||||
|
m_physicalSize = QAndroidPlatformIntegration::m_defaultPhysicalSize;
|
||||||
|
|
||||||
// Raster only apps should set QT_ANDROID_RASTER_IMAGE_DEPTH to 16
|
// Raster only apps should set QT_ANDROID_RASTER_IMAGE_DEPTH to 16
|
||||||
// is way much faster than 32
|
// is way much faster than 32
|
||||||
if (qEnvironmentVariableIntValue("QT_ANDROID_RASTER_IMAGE_DEPTH") == 16) {
|
if (qEnvironmentVariableIntValue("QT_ANDROID_RASTER_IMAGE_DEPTH") == 16) {
|
||||||
@ -66,54 +72,52 @@ QAndroidPlatformScreen::QAndroidPlatformScreen()
|
|||||||
m_format = QImage::Format_ARGB32_Premultiplied;
|
m_format = QImage::Format_ARGB32_Premultiplied;
|
||||||
m_depth = 32;
|
m_depth = 32;
|
||||||
}
|
}
|
||||||
m_physicalSize = QAndroidPlatformIntegration::m_defaultPhysicalSize;
|
|
||||||
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QAndroidPlatformScreen::applicationStateChanged);
|
|
||||||
|
|
||||||
QJniObject activity(QtAndroid::activity());
|
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this,
|
||||||
if (!activity.isValid())
|
&QAndroidPlatformScreen::applicationStateChanged);
|
||||||
return;
|
|
||||||
QJniObject display;
|
if (!displayObject.isValid())
|
||||||
if (QNativeInterface::QAndroidApplication::sdkVersion() < 30) {
|
|
||||||
display = activity.callObjectMethod("getWindowManager", "()Landroid/view/WindowManager;")
|
|
||||||
.callObjectMethod("getDefaultDisplay", "()Landroid/view/Display;");
|
|
||||||
} else {
|
|
||||||
display = activity.callObjectMethod("getDisplay", "()Landroid/view/Display;");
|
|
||||||
}
|
|
||||||
if (!display.isValid())
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_name = display.callObjectMethod("getName", "()Ljava/lang/String;").toString();
|
m_size = QSize(displayObject.callMethod<jint>("getWidth"), displayObject.callMethod<jint>("getHeight"));
|
||||||
m_refreshRate = display.callMethod<jfloat>("getRefreshRate");
|
m_name = displayObject.callObjectMethod<jstring>("getName").toString();
|
||||||
|
m_refreshRate = displayObject.callMethod<jfloat>("getRefreshRate");
|
||||||
|
|
||||||
if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) {
|
if (QNativeInterface::QAndroidApplication::sdkVersion() >= 23) {
|
||||||
m_modes << Mode { .size = m_physicalSize.toSize(), .refreshRate = m_refreshRate };
|
const QJniObject currentMode = displayObject.callObjectMethod<QtJniTypes::DisplayMode>("getMode");
|
||||||
return;
|
const jint currentModeId = currentMode.callMethod<jint>("getModeId");
|
||||||
|
|
||||||
|
const QJniObject supportedModes = displayObject.callObjectMethod<QtJniTypes::DisplayMode[]>(
|
||||||
|
"getSupportedModes");
|
||||||
|
const auto modeArray = jobjectArray(supportedModes.object());
|
||||||
|
|
||||||
|
QJniEnvironment env;
|
||||||
|
const auto size = env->GetArrayLength(modeArray);
|
||||||
|
for (jsize i = 0; i < size; ++i) {
|
||||||
|
const auto mode = QJniObject::fromLocalRef(env->GetObjectArrayElement(modeArray, i));
|
||||||
|
const int physicalWidth = mode.callMethod<jint>("getPhysicalWidth");
|
||||||
|
const int physicalHeight = mode.callMethod<jint>("getPhysicalHeight");
|
||||||
|
|
||||||
|
if (currentModeId == mode.callMethod<jint>("getModeId")) {
|
||||||
|
m_currentMode = i;
|
||||||
|
m_physicalSize = QSize {
|
||||||
|
physicalWidth,
|
||||||
|
physicalHeight
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
m_modes << QPlatformScreen::Mode {
|
||||||
|
.size = QSize { physicalWidth, physicalHeight },
|
||||||
|
.refreshRate = mode.callMethod<jfloat>("getRefreshRate")
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QJniEnvironment env;
|
|
||||||
const jint currentMode = display.callObjectMethod("getMode", "()Landroid/view/Display$Mode;")
|
|
||||||
.callMethod<jint>("getModeId");
|
|
||||||
const auto modes = display.callObjectMethod("getSupportedModes",
|
|
||||||
"()[Landroid/view/Display$Mode;");
|
|
||||||
const auto modesArray = jobjectArray(modes.object());
|
|
||||||
const auto sz = env->GetArrayLength(modesArray);
|
|
||||||
for (jsize i = 0; i < sz; ++i) {
|
|
||||||
auto mode = QJniObject::fromLocalRef(env->GetObjectArrayElement(modesArray, i));
|
|
||||||
if (currentMode == mode.callMethod<jint>("getModeId"))
|
|
||||||
m_currentMode = m_modes.size();
|
|
||||||
m_modes << Mode { .size = QSize { mode.callMethod<jint>("getPhysicalHeight"),
|
|
||||||
mode.callMethod<jint>("getPhysicalWidth") },
|
|
||||||
.refreshRate = mode.callMethod<jfloat>("getRefreshRate") };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_modes.isEmpty())
|
|
||||||
m_modes << Mode { .size = m_physicalSize.toSize(), .refreshRate = m_refreshRate };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QAndroidPlatformScreen::~QAndroidPlatformScreen()
|
QAndroidPlatformScreen::~QAndroidPlatformScreen()
|
||||||
{
|
{
|
||||||
if (m_id != -1) {
|
if (m_surfaceId != -1) {
|
||||||
QtAndroid::destroySurface(m_id);
|
QtAndroid::destroySurface(m_surfaceId);
|
||||||
m_surfaceWaitCondition.wakeOne();
|
m_surfaceWaitCondition.wakeOne();
|
||||||
releaseSurface();
|
releaseSurface();
|
||||||
}
|
}
|
||||||
@ -304,9 +308,9 @@ void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_id != -1) {
|
if (m_surfaceId != -1) {
|
||||||
releaseSurface();
|
releaseSurface();
|
||||||
QtAndroid::setSurfaceGeometry(m_id, rect);
|
QtAndroid::setSurfaceGeometry(m_surfaceId, rect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,8 +321,8 @@ void QAndroidPlatformScreen::applicationStateChanged(Qt::ApplicationState state)
|
|||||||
|
|
||||||
if (state <= Qt::ApplicationHidden) {
|
if (state <= Qt::ApplicationHidden) {
|
||||||
lockSurface();
|
lockSurface();
|
||||||
QtAndroid::destroySurface(m_id);
|
QtAndroid::destroySurface(m_surfaceId);
|
||||||
m_id = -1;
|
m_surfaceId = -1;
|
||||||
releaseSurface();
|
releaseSurface();
|
||||||
unlockSurface();
|
unlockSurface();
|
||||||
}
|
}
|
||||||
@ -361,17 +365,17 @@ void QAndroidPlatformScreen::doRedraw(QImage* screenGrabImage)
|
|||||||
}
|
}
|
||||||
if (!hasVisibleRasterWindows) {
|
if (!hasVisibleRasterWindows) {
|
||||||
lockSurface();
|
lockSurface();
|
||||||
if (m_id != -1) {
|
if (m_surfaceId != -1) {
|
||||||
QtAndroid::destroySurface(m_id);
|
QtAndroid::destroySurface(m_surfaceId);
|
||||||
releaseSurface();
|
releaseSurface();
|
||||||
m_id = -1;
|
m_surfaceId = -1;
|
||||||
}
|
}
|
||||||
unlockSurface();
|
unlockSurface();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QMutexLocker lock(&m_surfaceMutex);
|
QMutexLocker lock(&m_surfaceMutex);
|
||||||
if (m_id == -1 && m_rasterSurfaces) {
|
if (m_surfaceId == -1 && m_rasterSurfaces) {
|
||||||
m_id = QtAndroid::createSurface(this, geometry(), true, m_depth);
|
m_surfaceId = QtAndroid::createSurface(this, geometry(), true, m_depth);
|
||||||
AndroidDeadlockProtector protector;
|
AndroidDeadlockProtector protector;
|
||||||
if (!protector.acquire())
|
if (!protector.acquire())
|
||||||
return;
|
return;
|
||||||
|
@ -24,7 +24,7 @@ class QAndroidPlatformScreen: public QObject, public QPlatformScreen, public And
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
QAndroidPlatformScreen();
|
QAndroidPlatformScreen(const QJniObject &displayObject);
|
||||||
~QAndroidPlatformScreen();
|
~QAndroidPlatformScreen();
|
||||||
|
|
||||||
QRect geometry() const override { return QRect(QPoint(), m_size); }
|
QRect geometry() const override { return QRect(QPoint(), m_size); }
|
||||||
@ -38,7 +38,6 @@ public:
|
|||||||
int currentMode() const override { return m_currentMode; }
|
int currentMode() const override { return m_currentMode; }
|
||||||
int preferredMode() const override { return m_currentMode; }
|
int preferredMode() const override { return m_currentMode; }
|
||||||
qreal refreshRate() const override { return m_refreshRate; }
|
qreal refreshRate() const override { return m_refreshRate; }
|
||||||
|
|
||||||
inline QWindow *topWindow() const;
|
inline QWindow *topWindow() const;
|
||||||
QWindow *topLevelAt(const QPoint & p) const override;
|
QWindow *topLevelAt(const QPoint & p) const override;
|
||||||
|
|
||||||
@ -94,7 +93,7 @@ private slots:
|
|||||||
void doRedraw(QImage *screenGrabImage = nullptr);
|
void doRedraw(QImage *screenGrabImage = nullptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_id = -1;
|
int m_surfaceId = -1;
|
||||||
QAtomicInt m_rasterSurfaces = 0;
|
QAtomicInt m_rasterSurfaces = 0;
|
||||||
ANativeWindow* m_nativeSurface = nullptr;
|
ANativeWindow* m_nativeSurface = nullptr;
|
||||||
QWaitCondition m_surfaceWaitCondition;
|
QWaitCondition m_surfaceWaitCondition;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user