eglfs: Add support for raster content rotation
Running QWidget or other raster window-based apps with QT_QPA_EGLFS_ROTATION=180 will now flip the screen as expected. In addition, 90 and -90 are supported too. These will affect the reported screen geometry as well. The OpenGL mouse cursor is repositioned and rotated accordingly. For true OpenGL content the rotation is ignored. Hardware mouse cursors (DRM) ignore it as well for the time being. [ChangeLog][Platform Specific Changes] QWidget-based applications running on the eglfs platform plugin can now request 180 or 90 degrees rotated output by setting the QT_QPA_EGLFS_ROTATION environment variable. Task-number: QTBUG-39959 Change-Id: I3570b515d0233c6c6a0e813899b935ee222eab62 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
parent
97c00e66c8
commit
c4b4eba188
@ -40,7 +40,6 @@
|
||||
#include <QtGui/QOpenGLContext>
|
||||
#include <QtGui/QOpenGLFramebufferObject>
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtGui/QMatrix4x4>
|
||||
#include <qpa/qplatformbackingstore.h>
|
||||
|
||||
#include "qopenglcompositor_p.h"
|
||||
@ -78,7 +77,8 @@ static QOpenGLCompositor *compositor = 0;
|
||||
|
||||
QOpenGLCompositor::QOpenGLCompositor()
|
||||
: m_context(0),
|
||||
m_targetWindow(0)
|
||||
m_targetWindow(0),
|
||||
m_rotation(0)
|
||||
{
|
||||
Q_ASSERT(!compositor);
|
||||
m_updateTimer.setSingleShot(true);
|
||||
@ -93,10 +93,19 @@ QOpenGLCompositor::~QOpenGLCompositor()
|
||||
compositor = 0;
|
||||
}
|
||||
|
||||
void QOpenGLCompositor::setTarget(QOpenGLContext *context, QWindow *targetWindow)
|
||||
void QOpenGLCompositor::setTarget(QOpenGLContext *context, QWindow *targetWindow,
|
||||
const QRect &nativeTargetGeometry)
|
||||
{
|
||||
m_context = context;
|
||||
m_targetWindow = targetWindow;
|
||||
m_nativeTargetGeometry = nativeTargetGeometry;
|
||||
}
|
||||
|
||||
void QOpenGLCompositor::setRotation(int degrees)
|
||||
{
|
||||
m_rotation = degrees;
|
||||
m_rotationMatrix.setToIdentity();
|
||||
m_rotationMatrix.rotate(degrees, 0, 0, 1);
|
||||
}
|
||||
|
||||
void QOpenGLCompositor::update()
|
||||
@ -109,7 +118,7 @@ QImage QOpenGLCompositor::grab()
|
||||
{
|
||||
Q_ASSERT(m_context && m_targetWindow);
|
||||
m_context->makeCurrent(m_targetWindow);
|
||||
QScopedPointer<QOpenGLFramebufferObject> fbo(new QOpenGLFramebufferObject(m_targetWindow->geometry().size()));
|
||||
QScopedPointer<QOpenGLFramebufferObject> fbo(new QOpenGLFramebufferObject(m_nativeTargetGeometry.size()));
|
||||
renderAll(fbo.data());
|
||||
return fbo->toImage();
|
||||
}
|
||||
@ -127,9 +136,7 @@ void QOpenGLCompositor::renderAll(QOpenGLFramebufferObject *fbo)
|
||||
fbo->bind();
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
const QRect targetWindowRect(QPoint(0, 0), m_targetWindow->geometry().size());
|
||||
glViewport(0, 0, targetWindowRect.width(), targetWindowRect.height());
|
||||
glViewport(0, 0, m_nativeTargetGeometry.width(), m_nativeTargetGeometry.height());
|
||||
|
||||
if (!m_blitter.isCreated())
|
||||
m_blitter.create();
|
||||
@ -181,7 +188,8 @@ static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight)
|
||||
topLeftRect.width(), topLeftRect.height());
|
||||
}
|
||||
|
||||
static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRect &targetWindowRect, QOpenGLTextureBlitter *blitter)
|
||||
static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRect &targetWindowRect,
|
||||
QOpenGLTextureBlitter *blitter, QMatrix4x4 *rotationMatrix)
|
||||
{
|
||||
const QRect clipRect = textures->clipRect(idx);
|
||||
if (clipRect.isEmpty())
|
||||
@ -191,7 +199,10 @@ static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRe
|
||||
const QRect clippedRectInWindow = rectInWindow & clipRect.translated(rectInWindow.topLeft());
|
||||
const QRect srcRect = toBottomLeftRect(clipRect, rectInWindow.height());
|
||||
|
||||
const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(clippedRectInWindow, targetWindowRect);
|
||||
QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(clippedRectInWindow, targetWindowRect);
|
||||
if (rotationMatrix)
|
||||
target = *rotationMatrix * target;
|
||||
|
||||
const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(srcRect, rectInWindow.size(),
|
||||
QOpenGLTextureBlitter::OriginBottomLeft);
|
||||
|
||||
@ -219,25 +230,29 @@ void QOpenGLCompositor::render(QOpenGLCompositorWindow *window)
|
||||
if (textures->count() > 1 && i == textures->count() - 1) {
|
||||
// Backingstore for a widget with QOpenGLWidget subwidgets
|
||||
blend.set(true);
|
||||
const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
|
||||
QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
|
||||
if (m_rotation)
|
||||
target = m_rotationMatrix * target;
|
||||
m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft);
|
||||
} else if (textures->count() == 1) {
|
||||
// A regular QWidget window
|
||||
const bool translucent = window->sourceWindow()->requestedFormat().alphaBufferSize() > 0;
|
||||
blend.set(translucent);
|
||||
const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
|
||||
QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
|
||||
if (m_rotation)
|
||||
target = m_rotationMatrix * target;
|
||||
m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft);
|
||||
} else if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) {
|
||||
// Texture from an FBO belonging to a QOpenGLWidget
|
||||
blend.set(false);
|
||||
clippedBlit(textures, i, targetWindowRect, &m_blitter);
|
||||
clippedBlit(textures, i, targetWindowRect, &m_blitter, m_rotation ? &m_rotationMatrix : nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < textures->count(); ++i) {
|
||||
if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) {
|
||||
blend.set(true);
|
||||
clippedBlit(textures, i, targetWindowRect, &m_blitter);
|
||||
clippedBlit(textures, i, targetWindowRect, &m_blitter, m_rotation ? &m_rotationMatrix : nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,7 @@
|
||||
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtGui/QOpenGLTextureBlitter>
|
||||
#include <QtGui/QMatrix4x4>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@ -78,7 +79,8 @@ public:
|
||||
static QOpenGLCompositor *instance();
|
||||
static void destroy();
|
||||
|
||||
void setTarget(QOpenGLContext *context, QWindow *window);
|
||||
void setTarget(QOpenGLContext *context, QWindow *window, const QRect &nativeTargetGeometry);
|
||||
void setRotation(int degrees);
|
||||
QOpenGLContext *context() const { return m_context; }
|
||||
QWindow *targetWindow() const { return m_targetWindow; }
|
||||
|
||||
@ -106,6 +108,9 @@ private:
|
||||
|
||||
QOpenGLContext *m_context;
|
||||
QWindow *m_targetWindow;
|
||||
QRect m_nativeTargetGeometry;
|
||||
int m_rotation;
|
||||
QMatrix4x4 m_rotationMatrix;
|
||||
QTimer m_updateTimer;
|
||||
QOpenGLTextureBlitter m_blitter;
|
||||
QList<QOpenGLCompositorWindow *> m_windows;
|
||||
|
@ -72,6 +72,10 @@ QEglFSCursor::QEglFSCursor(QPlatformScreen *screen)
|
||||
if (!m_visible)
|
||||
return;
|
||||
|
||||
int rotation = qEnvironmentVariableIntValue("QT_QPA_EGLFS_ROTATION");
|
||||
if (rotation)
|
||||
m_rotationMatrix.rotate(rotation, 0, 0, 1);
|
||||
|
||||
// Try to load the cursor atlas. If this fails, m_visible is set to false and
|
||||
// paintOnScreen() and setCurrentCursor() become no-ops.
|
||||
initCursorAtlas();
|
||||
@ -129,9 +133,10 @@ void QEglFSCursor::createShaderPrograms()
|
||||
"attribute highp vec2 vertexCoordEntry;\n"
|
||||
"attribute highp vec2 textureCoordEntry;\n"
|
||||
"varying highp vec2 textureCoord;\n"
|
||||
"uniform highp mat4 mat;\n"
|
||||
"void main() {\n"
|
||||
" textureCoord = textureCoordEntry;\n"
|
||||
" gl_Position = vec4(vertexCoordEntry, 1.0, 1.0);\n"
|
||||
" gl_Position = mat * vec4(vertexCoordEntry, 1.0, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
static const char *textureFragmentProgram =
|
||||
@ -149,6 +154,7 @@ void QEglFSCursor::createShaderPrograms()
|
||||
m_program->link();
|
||||
|
||||
m_textureEntry = m_program->uniformLocation("texture");
|
||||
m_matEntry = m_program->uniformLocation("mat");
|
||||
}
|
||||
|
||||
void QEglFSCursor::createCursorTexture(uint *texture, const QImage &image)
|
||||
@ -492,6 +498,7 @@ void QEglFSCursor::draw(const QRectF &r)
|
||||
m_program->setAttributeArray(1, textureCoordinates, 2);
|
||||
|
||||
m_program->setUniformValue(m_textureEntry, 0);
|
||||
m_program->setUniformValue(m_matEntry, m_rotationMatrix);
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
glFrontFace(GL_CCW);
|
||||
|
@ -54,6 +54,7 @@
|
||||
#include "qeglfsglobal.h"
|
||||
#include <qpa/qplatformcursor.h>
|
||||
#include <qpa/qplatformscreen.h>
|
||||
#include <QtGui/QMatrix4x4>
|
||||
#include <QtGui/QOpenGLFunctions>
|
||||
#include <QtGui/private/qinputdevicemanager_p.h>
|
||||
|
||||
@ -138,8 +139,10 @@ private:
|
||||
QEglFSScreen *m_screen;
|
||||
QOpenGLShaderProgram *m_program;
|
||||
int m_textureEntry;
|
||||
int m_matEntry;
|
||||
QEglFSCursorDeviceListener *m_deviceListener;
|
||||
bool m_updateRequested;
|
||||
QMatrix4x4 m_rotationMatrix;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -64,6 +64,31 @@ QEglFSScreen::~QEglFSScreen()
|
||||
}
|
||||
|
||||
QRect QEglFSScreen::geometry() const
|
||||
{
|
||||
QRect r = geometryForSurface();
|
||||
|
||||
static int rotation = qEnvironmentVariableIntValue("QT_QPA_EGLFS_ROTATION");
|
||||
switch (rotation) {
|
||||
case 0:
|
||||
case 180:
|
||||
case -180:
|
||||
break;
|
||||
case 90:
|
||||
case -90: {
|
||||
int h = r.height();
|
||||
r.setHeight(r.width());
|
||||
r.setWidth(h);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
qWarning("Invalid rotation %d specified in QT_QPA_EGLFS_ROTATION", rotation);
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
QRect QEglFSScreen::geometryForSurface() const
|
||||
{
|
||||
return QRect(QPoint(0, 0), qt_egl_device_integration()->screenSize());
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ public:
|
||||
~QEglFSScreen();
|
||||
|
||||
QRect geometry() const Q_DECL_OVERRIDE;
|
||||
QRect geometryForSurface() const;
|
||||
int depth() const Q_DECL_OVERRIDE;
|
||||
QImage::Format format() const Q_DECL_OVERRIDE;
|
||||
|
||||
|
@ -142,7 +142,8 @@ void QEglFSWindow::create()
|
||||
context->setScreen(window()->screen());
|
||||
if (Q_UNLIKELY(!context->create()))
|
||||
qFatal("EGLFS: Failed to create compositing context");
|
||||
compositor->setTarget(context, window());
|
||||
compositor->setTarget(context, window(), screen->geometryForSurface());
|
||||
compositor->setRotation(qEnvironmentVariableIntValue("QT_QPA_EGLFS_ROTATION"));
|
||||
// If there is a "root" window into which raster and QOpenGLWidget content is
|
||||
// composited, all other contexts must share with its context.
|
||||
if (!qt_gl_global_share_context()) {
|
||||
@ -189,7 +190,8 @@ void QEglFSWindow::resetSurface()
|
||||
|
||||
m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
|
||||
m_format = q_glFormatFromConfig(display, m_config, platformFormat);
|
||||
m_window = qt_egl_device_integration()->createNativeWindow(this, screen()->geometry().size(), m_format);
|
||||
const QSize surfaceSize = screen()->geometryForSurface().size();
|
||||
m_window = qt_egl_device_integration()->createNativeWindow(this, surfaceSize, m_format);
|
||||
m_surface = eglCreateWindowSurface(display, m_config, m_window, NULL);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user