+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the Qt Compositor.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
#include "qwindowcompositor.h"
+
#include <QMouseEvent>
#include <QKeyEvent>
#include <QTouchEvent>
+#include <QOpenGLFunctions>
+#include <QGuiApplication>
+#include <QCursor>
+#include <QPixmap>
+#include <QLinkedList>
+#include <QScreen>
+
+#include <QtCompositor/waylandinput.h>
QWindowCompositor::QWindowCompositor(QOpenGLWindow *window)
: WaylandCompositor(window)
, m_window(window)
+ , m_textureBlitter(0)
+ , m_renderScheduler(this)
+ , m_draggingWindow(0)
+ , m_dragKeyIsPressed(false)
+ , m_modifiers(Qt::NoModifier)
{
+ enableSubSurfaceExtension();
+ m_window->makeCurrent();
+
+ m_textureCache = new QOpenGLTextureCache(m_window->context());
+ m_textureBlitter = new TextureBlitter();
m_backgroundImage = QImage(QLatin1String(":/background.jpg"));
- m_renderer = new SurfaceRenderer(m_window->context(), m_window);
- m_backgroundTexture = m_renderer->textureFromImage(m_backgroundImage);
+ m_renderScheduler.setSingleShot(true);
+ connect(&m_renderScheduler,SIGNAL(timeout()),this,SLOT(render()));
+
+ QOpenGLFunctions *functions = m_window->context()->functions();
+ functions->glGenFramebuffers(1, &m_surface_fbo);
window->installEventFilter(this);
- render();
+ setRetainedSelectionEnabled(true);
+
+ setOutputGeometry(QRect(QPoint(0, 0), window->size()));
+ setOutputRefreshRate(qGuiApp->primaryScreen()->refreshRate());
+}
+
+QWindowCompositor::~QWindowCompositor()
+{
+ delete m_textureBlitter;
+ delete m_textureCache;
}
void QWindowCompositor::surfaceDestroyed(QObject *object)
{
WaylandSurface *surface = static_cast<WaylandSurface *>(object);
m_surfaces.removeOne(surface);
- if (inputFocus() == surface || !inputFocus()) // typically reset to 0 already in Compositor::surfaceDestroyed()
- setInputFocus(m_surfaces.isEmpty() ? 0 : m_surfaces.last());
- render();
+ if (defaultInputDevice()->keyboardFocus() == surface || !defaultInputDevice()->keyboardFocus()) // typically reset to 0 already in Compositor::surfaceDestroyed()
+ defaultInputDevice()->setKeyboardFocus(m_surfaces.isEmpty() ? 0 : m_surfaces.last());
+ m_renderScheduler.start(0);
}
-void QWindowCompositor::surfaceMapped(const QSize &size)
+void QWindowCompositor::surfaceMapped()
{
WaylandSurface *surface = qobject_cast<WaylandSurface *>(sender());
QPoint pos;
if (!m_surfaces.contains(surface)) {
- uint px = 1 + (qrand() % (m_window->width() - size.width() - 2));
- uint py = 1 + (qrand() % (m_window->height() - size.height() - 2));
+ uint px = 0;
+ uint py = 0;
+ if (!QCoreApplication::arguments().contains(QLatin1String("-stickytopleft"))) {
+ px = 1 + (qrand() % (m_window->width() - surface->size().width() - 2));
+ py = 1 + (qrand() % (m_window->height() - surface->size().height() - 2));
+ }
pos = QPoint(px, py);
- surface->setGeometry(QRect(pos, size));
+ surface->setPos(pos);
} else {
- surface->setGeometry(QRect(window()->geometry().topLeft(),size));
m_surfaces.removeOne(surface);
}
m_surfaces.append(surface);
- setInputFocus(surface);
- render();
+ defaultInputDevice()->setKeyboardFocus(surface);
+ m_renderScheduler.start(0);
}
void QWindowCompositor::surfaceDamaged(const QRect &rect)
{
Q_UNUSED(surface)
Q_UNUSED(rect)
- render();
+ m_renderScheduler.start(0);
}
void QWindowCompositor::surfaceCreated(WaylandSurface *surface)
{
connect(surface, SIGNAL(destroyed(QObject *)), this, SLOT(surfaceDestroyed(QObject *)));
- connect(surface, SIGNAL(mapped(const QSize &)), this, SLOT(surfaceMapped(const QSize &)));
+ connect(surface, SIGNAL(mapped()), this, SLOT(surfaceMapped()));
connect(surface, SIGNAL(damaged(const QRect &)), this, SLOT(surfaceDamaged(const QRect &)));
- render();
+ connect(surface, SIGNAL(extendedSurfaceReady()), this, SLOT(sendExpose()));
+ m_renderScheduler.start(0);
+}
+
+void QWindowCompositor::sendExpose()
+{
+ WaylandSurface *surface = qobject_cast<WaylandSurface *>(sender());
+ surface->sendOnScreenVisibilityChange(true);
}
QPointF QWindowCompositor::toSurface(WaylandSurface *surface, const QPointF &pos) const
{
- return pos - surface->geometry().topLeft();
+ return pos - surface->pos();
+}
+
+void QWindowCompositor::changeCursor(const QImage &image, int hotspotX, int hotspotY)
+{
+ QCursor cursor(QPixmap::fromImage(image),hotspotX,hotspotY);
+ static bool cursroIsSet = false;
+ if (cursroIsSet) {
+ QGuiApplication::changeOverrideCursor(cursor);
+ } else {
+ QGuiApplication::setOverrideCursor(cursor);
+ cursroIsSet = true;
+ }
}
-WaylandSurface *QWindowCompositor::surfaceAt(const QPoint &point, QPoint *local)
+WaylandSurface *QWindowCompositor::surfaceAt(const QPointF &point, QPointF *local)
{
for (int i = m_surfaces.size() - 1; i >= 0; --i) {
- if (m_surfaces.at(i)->geometry().contains(point)) {
+ WaylandSurface *surface = m_surfaces.at(i);
+ QRectF geo(surface->pos(), surface->size());
+ if (geo.contains(point)) {
if (local)
- *local = toSurface(m_surfaces.at(i), point).toPoint();
- return m_surfaces.at(i);
+ *local = toSurface(surface, point);
+ return surface;
}
}
return 0;
}
+GLuint QWindowCompositor::composeSurface(WaylandSurface *surface)
+{
+ GLuint texture = 0;
+
+ QOpenGLFunctions *functions = QOpenGLContext::currentContext()->functions();
+ functions->glBindFramebuffer(GL_FRAMEBUFFER, m_surface_fbo);
+
+ if (surface->type() == WaylandSurface::Shm) {
+ texture = m_textureCache->bindTexture(QOpenGLContext::currentContext(),surface->image());
+ } else if (surface->type() == WaylandSurface::Texture) {
+ texture = surface->texture(QOpenGLContext::currentContext());
+ }
+
+ functions->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, texture, 0);
+ paintChildren(surface,surface);
+ functions->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,0, 0);
+
+ functions->glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ return texture;
+}
+
+void QWindowCompositor::paintChildren(WaylandSurface *surface, WaylandSurface *window) {
+
+ if (surface->subSurfaces().size() == 0)
+ return;
+
+ QLinkedListIterator<WaylandSurface *> i(surface->subSurfaces());
+ while (i.hasNext()) {
+ WaylandSurface *subSurface = i.next();
+ QPointF p = subSurface->mapTo(window,QPointF(0,0));
+ if (subSurface->size().isValid()) {
+ GLuint texture = 0;
+ if (subSurface->type() == WaylandSurface::Texture) {
+ texture = subSurface->texture(QOpenGLContext::currentContext());
+ } else if (surface->type() == WaylandSurface::Shm ) {
+ texture = m_textureCache->bindTexture(QOpenGLContext::currentContext(),surface->image());
+ }
+ QRect geo(p.toPoint(),subSurface->size());
+ m_textureBlitter->drawTexture(texture,geo,window->size(),0,window->isYInverted(),subSurface->isYInverted());
+ }
+ paintChildren(subSurface,window);
+ }
+}
+
+
void QWindowCompositor::render()
{
m_window->makeCurrent();
+ m_backgroundTexture = m_textureCache->bindTexture(QOpenGLContext::currentContext(),m_backgroundImage);
+ m_textureBlitter->bind();
//Draw the background Image texture
int w = m_window->width();
int h = m_window->height();
- int iw = m_backgroundImage.width();
- int ih = m_backgroundImage.height();
- for (int y = 0; y < h; y += ih) {
- for (int x = 0; x < w; x += iw) {
- m_renderer->drawTexture(m_backgroundTexture, QRect(QPoint(x, y), QSize(iw, ih)), 0);
+ QSize imageSize = m_backgroundImage.size();
+ for (int y = 0; y < h; y += imageSize.height()) {
+ for (int x = 0; x < w; x += imageSize.width()) {
+ m_textureBlitter->drawTexture(m_backgroundTexture,QRect(QPoint(x, y),imageSize),window()->size(), 0,true,true);
}
}
- //Iterate all surfaces in m_surfaces
- //If type == WaylandSurface::Texture draw textureId at geometry
foreach (WaylandSurface *surface, m_surfaces) {
- if (surface->type() == WaylandSurface::Texture)
- m_renderer->drawTexture(surface->texture(QOpenGLContext::currentContext()), surface->geometry(), 1); //depth argument should be dynamic (focused should be top).
- else if (surface->type() == WaylandSurface::Shm)
- m_renderer->drawImage(surface->image(), surface->geometry());
+ GLuint texture = composeSurface(surface);
+ QRect geo(surface->pos().toPoint(),surface->size());
+ m_textureBlitter->drawTexture(texture,geo,m_window->size(),0,false,surface->isYInverted());
}
+
+ m_textureBlitter->release();
frameFinished();
- glFinish();
+ // N.B. Never call glFinish() here as the busylooping with vsync 'feature' of the nvidia binary driver is not desirable.
m_window->swapBuffers();
- m_window->context()->doneCurrent();
}
bool QWindowCompositor::eventFilter(QObject *obj, QEvent *event)
if (obj != m_window)
return false;
+ WaylandInputDevice *input = defaultInputDevice();
+
switch (event->type()) {
case QEvent::Expose:
- render();
+ m_renderScheduler.start(0);
+ if (m_window->isExposed()) {
+ // Alt-tabbing away normally results in the alt remaining in
+ // pressed state in the clients xkb state. Prevent this by sending
+ // a release. This is not an issue in a "real" compositor but
+ // is very annoying when running in a regular window on xcb.
+ Qt::KeyboardModifiers mods = QGuiApplication::queryKeyboardModifiers();
+ if (m_modifiers != mods && input->keyboardFocus()) {
+ Qt::KeyboardModifiers stuckMods = m_modifiers ^ mods;
+ if (stuckMods & Qt::AltModifier)
+ input->sendKeyReleaseEvent(64); // native scancode for left alt
+ m_modifiers = mods;
+ }
+ }
break;
case QEvent::MouseButtonPress: {
- QPoint local;
+ QPointF local;
QMouseEvent *me = static_cast<QMouseEvent *>(event);
- WaylandSurface *targetSurface = surfaceAt(me->pos(), &local);
- if (targetSurface) {
- if (inputFocus() != targetSurface) {
- setInputFocus(targetSurface);
+ WaylandSurface *targetSurface = surfaceAt(me->localPos(), &local);
+ if (m_dragKeyIsPressed && targetSurface) {
+ m_draggingWindow = targetSurface;
+ m_drag_diff = local;
+ } else {
+ if (targetSurface && input->keyboardFocus() != targetSurface) {
+ input->setKeyboardFocus(targetSurface);
m_surfaces.removeOne(targetSurface);
m_surfaces.append(targetSurface);
- render();
+ m_renderScheduler.start(0);
}
- targetSurface->sendMousePressEvent(local, me->button());
+ input->sendMousePressEvent(me->button(), local, me->localPos());
}
- break;
+ return true;
}
case QEvent::MouseButtonRelease: {
- WaylandSurface *targetSurface = inputFocus();
- if (targetSurface) {
+ WaylandSurface *targetSurface = input->mouseFocus();
+ if (m_draggingWindow) {
+ m_draggingWindow = 0;
+ m_drag_diff = QPointF();
+ } else {
QMouseEvent *me = static_cast<QMouseEvent *>(event);
- targetSurface->sendMouseReleaseEvent(toSurface(targetSurface, me->pos()).toPoint(), me->button());
+ QPointF localPos;
+ if (targetSurface)
+ localPos = toSurface(targetSurface, me->localPos());
+ input->sendMouseReleaseEvent(me->button(), localPos, me->localPos());
+ }
+ return true;
+ }
+ case QEvent::MouseMove: {
+ QMouseEvent *me = static_cast<QMouseEvent *>(event);
+ if (m_draggingWindow) {
+ m_draggingWindow->setPos(me->localPos() - m_drag_diff);
+ m_renderScheduler.start(0);
+ } else {
+ QPointF local;
+ WaylandSurface *targetSurface = surfaceAt(me->localPos(), &local);
+ input->sendMouseMoveEvent(targetSurface, local, me->localPos());
}
break;
}
+ case QEvent::Wheel: {
+ QWheelEvent *we = static_cast<QWheelEvent *>(event);
+ input->sendMouseWheelEvent(we->orientation(), we->delta());
+ break;
+ }
case QEvent::KeyPress: {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
- WaylandSurface *targetSurface = inputFocus();
+ if (ke->key() == Qt::Key_Meta || ke->key() == Qt::Key_Super_L) {
+ m_dragKeyIsPressed = true;
+ }
+ m_modifiers = ke->modifiers();
+ WaylandSurface *targetSurface = input->keyboardFocus();
if (targetSurface)
- targetSurface->sendKeyPressEvent(ke->nativeScanCode());
+ input->sendKeyPressEvent(ke->nativeScanCode());
break;
}
case QEvent::KeyRelease: {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
- WaylandSurface *targetSurface = inputFocus();
+ if (ke->key() == Qt::Key_Meta || ke->key() == Qt::Key_Super_L) {
+ m_dragKeyIsPressed = false;
+ }
+ m_modifiers = ke->modifiers();
+ WaylandSurface *targetSurface = input->keyboardFocus();
if (targetSurface)
- targetSurface->sendKeyReleaseEvent(ke->nativeScanCode());
+ input->sendKeyReleaseEvent(ke->nativeScanCode());
break;
}
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
{
- QSet<WaylandSurface *> targets;
+ WaylandSurface *targetSurface = 0;
QTouchEvent *te = static_cast<QTouchEvent *>(event);
QList<QTouchEvent::TouchPoint> points = te->touchPoints();
- for (int i = 0; i < points.count(); ++i) {
- const QTouchEvent::TouchPoint &tp(points.at(i));
- QPoint local;
- WaylandSurface *targetSurface = surfaceAt(tp.pos().toPoint(), &local);
- if (targetSurface) {
- targetSurface->sendTouchPointEvent(tp.id(), local.x(), local.y(), tp.state());
- targets.insert(targetSurface);
- }
+ QPoint pointPos;
+ if (!points.isEmpty()) {
+ pointPos = points.at(0).pos().toPoint();
+ targetSurface = surfaceAt(pointPos);
}
- foreach (WaylandSurface *surface, targets)
- surface->sendTouchFrameEvent();
+ if (targetSurface && targetSurface != input->mouseFocus())
+ input->setMouseFocus(targetSurface, pointPos, pointPos);
+ if (input->mouseFocus())
+ input->sendFullTouchEvent(te);
break;
}
default: