Merge remote-tracking branch 'origin/5.6' into 5.7

Change-Id: I9cfefaf22b010fca937be77979f5fb50574bb71e
This commit is contained in:
Liang Qi 2016-09-21 07:14:33 +02:00
commit d10e4c193b
18 changed files with 332 additions and 121 deletions

View File

@ -0,0 +1,49 @@
/****************************************************************************
**
** Copyright (C) 2016 Samuel Gaist <samuel.gaist@edeltech.ch>
** Contact: http://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $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 The Qt Company Ltd 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$
**
****************************************************************************/
//! [0]
#include <QAbstractNativeEventFilter>
class MyCocoaEventFilter : public QAbstractNativeEventFilter
{
public:
bool nativeEventFilter(const QByteArray &eventType, void *message, long *) Q_DECL_OVERRIDE;
};
//! [0]

View File

@ -0,0 +1,57 @@
/****************************************************************************
**
** Copyright (C) 2016 Samuel Gaist <samuel.gaist@edeltech.ch>
** Contact: http://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $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 The Qt Company Ltd 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$
**
****************************************************************************/
//! [0]
#include "mycocoaeventfilter.h"
#import <AppKit/AppKit.h>
bool CocoaNativeEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *)
{
if (eventType == "mac_generic_NSEvent") {
NSEvent *event = static_cast<NSEvent *>(message);
if ([event type] == NSKeyDown) {
// Handle key event
qDebug() << QString::fromNSString([event characters]);
}
}
return false;
}
//! [0]

View File

@ -0,0 +1,5 @@
#! [0]
HEADERS += mycocoaeventfilter.h
OBJECTIVE_SOURCES += mycocoaeventfilter.mm
LIBS += -framework AppKit
#! [0]

View File

@ -83,7 +83,7 @@ QT_BEGIN_NAMESPACE
/*!
\property QAbstractProxyModel::sourceModel
\brief the source model this proxy model.
\brief the source model of this proxy model.
*/
//detects the deletion of the source model

View File

@ -96,14 +96,25 @@ QAbstractNativeEventFilter::~QAbstractNativeEventFilter()
In both cases, the \a message can be casted to a MSG pointer.
The \a result pointer is only used on Windows, and corresponds to the LRESULT pointer.
On Mac, \a eventType is set to "mac_generic_NSEvent", and the \a message can be casted to an EventRef.
On macOS, \a eventType is set to "mac_generic_NSEvent", and the \a message can be casted to an NSEvent pointer.
In your reimplementation of this function, if you want to filter
the \a message out, i.e. stop it being handled further, return
true; otherwise return false.
Example:
\b {Linux example}
\snippet code/src_corelib_kernel_qabstractnativeeventfilter.cpp 0
\b {macOS example}
mycocoaeventfilter.h:
\snippet code/src_corelib_kernel_qabstractnativeeventfilter.h 0
mycocoaeventfilter.mm:
\snippet code/src_corelib_kernel_qabstractnativeeventfilter.mm 0
myapp.pro:
\snippet code/src_corelib_kernel_qabstractnativeeventfilter.pro 0
*/
QT_END_NAMESPACE

View File

@ -585,7 +585,7 @@ void QBrush::detach(Qt::BrushStyle newStyle)
if (newStyle == d->style && d->ref.load() == 1)
return;
QScopedPointer<QBrushData> x;
QScopedPointer<QBrushData, QBrushDataPointerDeleter> x;
switch(newStyle) {
case Qt::TexturePattern: {
QTexturedBrushData *tbd = new QTexturedBrushData;
@ -601,28 +601,30 @@ void QBrush::detach(Qt::BrushStyle newStyle)
}
case Qt::LinearGradientPattern:
case Qt::RadialGradientPattern:
case Qt::ConicalGradientPattern:
x.reset(new QGradientBrushData);
case Qt::ConicalGradientPattern: {
QGradientBrushData *gbd = new QGradientBrushData;
switch (d->style) {
case Qt::LinearGradientPattern:
case Qt::RadialGradientPattern:
case Qt::ConicalGradientPattern:
static_cast<QGradientBrushData *>(x.data())->gradient =
gbd->gradient =
static_cast<QGradientBrushData *>(d.data())->gradient;
break;
default:
break;
}
x.reset(gbd);
break;
}
default:
x.reset(new QBrushData);
break;
}
x->ref.store(1);
x->ref.store(1); // must be first lest the QBrushDataPointerDeleter turns into a no-op
x->style = newStyle;
x->color = d->color;
x->transform = d->transform;
d.reset(x.take());
d.swap(x);
}

View File

@ -59,8 +59,15 @@ Q_LOGGING_CATEGORY(qLcEvdevKeyMap, "qt.qpa.input.keymap")
// simple builtin US keymap
#include "qevdevkeyboard_defaultmap_p.h"
QEvdevKeyboardHandler::QEvdevKeyboardHandler(const QString &device, int fd, bool disableZap, bool enableCompose, const QString &keymapFile)
: m_device(device), m_fd(fd), m_notify(Q_NULLPTR),
void QFdContainer::reset() Q_DECL_NOTHROW
{
if (m_fd >= 0)
qt_safe_close(m_fd);
m_fd = -1;
}
QEvdevKeyboardHandler::QEvdevKeyboardHandler(const QString &device, QFdContainer &fd, bool disableZap, bool enableCompose, const QString &keymapFile)
: m_device(device), m_fd(fd.release()), m_notify(Q_NULLPTR),
m_modifiers(0), m_composing(0), m_dead_unicode(0xffff),
m_no_zap(disableZap), m_do_compose(enableCompose),
m_keymap(0), m_keymap_size(0), m_keycompose(0), m_keycompose_size(0)
@ -75,16 +82,13 @@ QEvdevKeyboardHandler::QEvdevKeyboardHandler(const QString &device, int fd, bool
unloadKeymap();
// socket notifier for events on the keyboard device
m_notify = new QSocketNotifier(m_fd, QSocketNotifier::Read, this);
m_notify = new QSocketNotifier(m_fd.get(), QSocketNotifier::Read, this);
connect(m_notify, SIGNAL(activated(int)), this, SLOT(readKeycode()));
}
QEvdevKeyboardHandler::~QEvdevKeyboardHandler()
{
unloadKeymap();
if (m_fd >= 0)
qt_safe_close(m_fd);
}
QEvdevKeyboardHandler *QEvdevKeyboardHandler::create(const QString &device,
@ -118,13 +122,12 @@ QEvdevKeyboardHandler *QEvdevKeyboardHandler::create(const QString &device,
qCDebug(qLcEvdevKey) << "Opening keyboard at" << device;
int fd;
fd = qt_safe_open(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0);
if (fd >= 0) {
::ioctl(fd, EVIOCGRAB, grab);
QFdContainer fd(qt_safe_open(device.toLocal8Bit().constData(), O_RDONLY | O_NDELAY, 0));
if (fd.get() >= 0) {
::ioctl(fd.get(), EVIOCGRAB, grab);
if (repeatDelay > 0 && repeatRate > 0) {
int kbdrep[2] = { repeatDelay, repeatRate };
::ioctl(fd, EVIOCSREP, kbdrep);
::ioctl(fd.get(), EVIOCSREP, kbdrep);
}
return new QEvdevKeyboardHandler(device, fd, disableZap, enableCompose, keymapFile);
@ -144,7 +147,7 @@ void QEvdevKeyboardHandler::switchLed(int led, bool state)
led_ie.code = led;
led_ie.value = state;
qt_safe_write(m_fd, &led_ie, sizeof(led_ie));
qt_safe_write(m_fd.get(), &led_ie, sizeof(led_ie));
}
void QEvdevKeyboardHandler::readKeycode()
@ -153,7 +156,7 @@ void QEvdevKeyboardHandler::readKeycode()
int n = 0;
forever {
int result = qt_safe_read(m_fd, reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n);
int result = qt_safe_read(m_fd.get(), reinterpret_cast<char *>(buffer) + n, sizeof(buffer) - n);
if (result == 0) {
qWarning("evdevkeyboard: Got EOF from the input device");
@ -166,8 +169,7 @@ void QEvdevKeyboardHandler::readKeycode()
if (errno == ENODEV) {
delete m_notify;
m_notify = Q_NULLPTR;
qt_safe_close(m_fd);
m_fd = -1;
m_fd.reset();
}
return;
}
@ -478,7 +480,7 @@ void QEvdevKeyboardHandler::unloadKeymap()
//Set locks according to keyboard leds
quint16 ledbits[1];
memset(ledbits, 0, sizeof(ledbits));
if (::ioctl(m_fd, EVIOCGLED(sizeof(ledbits)), ledbits) < 0) {
if (::ioctl(m_fd.get(), EVIOCGLED(sizeof(ledbits)), ledbits) < 0) {
qWarning("evdevkeyboard: Failed to query led states");
switchLed(LED_NUML,false);
switchLed(LED_CAPSL, false);

View File

@ -129,12 +129,25 @@ inline QDataStream &operator<<(QDataStream &ds, const QEvdevKeyboardMap::Composi
return ds << c.first << c.second << c.result;
}
class QFdContainer
{
int m_fd;
Q_DISABLE_COPY(QFdContainer);
public:
explicit QFdContainer(int fd = -1) Q_DECL_NOTHROW : m_fd(fd) {}
~QFdContainer() { reset(); }
int get() const Q_DECL_NOTHROW { return m_fd; }
int release() Q_DECL_NOTHROW { int result = m_fd; m_fd = -1; return result; }
void reset() Q_DECL_NOTHROW;
};
class QEvdevKeyboardHandler : public QObject
{
Q_OBJECT
public:
QEvdevKeyboardHandler(const QString &device, int fd, bool disableZap, bool enableCompose, const QString &keymapFile);
QEvdevKeyboardHandler(const QString &device, QFdContainer &fd, bool disableZap, bool enableCompose, const QString &keymapFile);
~QEvdevKeyboardHandler();
enum KeycodeAction {
@ -187,7 +200,7 @@ private:
void switchLed(int, bool);
QString m_device;
int m_fd;
QFdContainer m_fd;
QSocketNotifier *m_notify;
// keymap handling

View File

@ -252,6 +252,8 @@ QStringList QConnmanManagerInterface::getServices()
bool QConnmanManagerInterface::requestScan(const QString &type)
{
bool scanned = false;
if (technologiesMap.isEmpty())
getTechnologies();
Q_FOREACH (QConnmanTechnologyInterface *tech, technologiesMap) {
if (tech->type() == type) {
tech->scan();

View File

@ -330,6 +330,11 @@ void QCocoaGLContext::updateSurfaceFormat()
[pixelFormat release];
GLint swapInterval = -1;
[m_context getValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
if (swapInterval >= 0)
m_format.setSwapInterval(swapInterval);
// Restore the original context
CGLSetCurrentContext(oldContext);
}

View File

@ -319,7 +319,7 @@ QIOSMenu::QIOSMenu()
: QPlatformMenu()
, m_tag(0)
, m_enabled(true)
, m_visible(true)
, m_visible(false)
, m_text(QString())
, m_menuType(DefaultMenu)
, m_effectiveMenuType(DefaultMenu)
@ -412,7 +412,7 @@ void QIOSMenu::handleItemSelected(QIOSMenuItem *menuItem)
void QIOSMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item)
{
if (m_currentMenu == this || !m_visible || !m_enabled || !parentWindow)
if (m_currentMenu == this || !parentWindow)
return;
emit aboutToShow();
@ -439,6 +439,8 @@ void QIOSMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect, c
toggleShowUsingUIPickerView(true);
break;
}
m_visible = true;
}
void QIOSMenu::dismiss()
@ -460,6 +462,7 @@ void QIOSMenu::dismiss()
}
m_currentMenu = 0;
m_visible = false;
}
void QIOSMenu::toggleShowUsingUIMenuController(bool show)

View File

@ -228,6 +228,10 @@
@implementation QIOSViewController
@synthesize prefersStatusBarHidden;
@synthesize preferredStatusBarUpdateAnimation;
@synthesize preferredStatusBarStyle;
- (id)initWithQIOSScreen:(QIOSScreen *)screen
{
if (self = [self init]) {

View File

@ -91,7 +91,7 @@ QIOSWindow::~QIOSWindow()
// practice this doesn't seem to happen when removing the view from its superview. To ensure that
// Qt's internal state for touch and mouse handling is kept consistent, we therefor have to force
// cancellation of all touch events.
[m_view touchesCancelled:0 withEvent:0];
[m_view touchesCancelled:[NSSet set] withEvent:0];
clearAccessibleCache();
m_view->m_qioswindow = 0;

View File

@ -423,7 +423,8 @@
// We do this by assuming that there are no cases where a
// sub-set of the active touch events are intentionally cancelled.
if (touches && (static_cast<NSInteger>([touches count]) != m_activeTouches.count()))
NSInteger count = static_cast<NSInteger>([touches count]);
if (count != 0 && count != m_activeTouches.count())
qWarning("Subset of active touches cancelled by UIKit");
m_activeTouches.clear();

View File

@ -748,8 +748,7 @@ void QXcbKeyboard::updateKeymap()
// update xkb state object
xkb_state_unref(xkb_state);
xkb_state = new_state;
if (!connection()->hasXKB())
updateXKBMods();
updateXKBMods();
checkForLatinLayout();
}
@ -774,32 +773,37 @@ void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state)
}
#endif
void QXcbKeyboard::updateXKBStateFromState(struct xkb_state *kb_state, quint16 state)
{
const quint32 modsDepressed = xkb_state_serialize_mods(kb_state, XKB_STATE_MODS_DEPRESSED);
const quint32 modsLatched = xkb_state_serialize_mods(kb_state, XKB_STATE_MODS_LATCHED);
const quint32 modsLocked = xkb_state_serialize_mods(kb_state, XKB_STATE_MODS_LOCKED);
const quint32 xkbMask = xkbModMask(state);
const quint32 latched = modsLatched & xkbMask;
const quint32 locked = modsLocked & xkbMask;
quint32 depressed = modsDepressed & xkbMask;
// set modifiers in depressed if they don't appear in any of the final masks
depressed |= ~(depressed | latched | locked) & xkbMask;
const xkb_state_component newState
= xkb_state_update_mask(kb_state,
depressed,
latched,
locked,
0,
0,
(state >> 13) & 3); // bits 13 and 14 report the state keyboard group
if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) {
//qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");
}
}
void QXcbKeyboard::updateXKBStateFromCore(quint16 state)
{
if (m_config && !connection()->hasXKB()) {
const quint32 modsDepressed = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_DEPRESSED);
const quint32 modsLatched = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LATCHED);
const quint32 modsLocked = xkb_state_serialize_mods(xkb_state, XKB_STATE_MODS_LOCKED);
const quint32 xkbMask = xkbModMask(state);
const quint32 latched = modsLatched & xkbMask;
const quint32 locked = modsLocked & xkbMask;
quint32 depressed = modsDepressed & xkbMask;
// set modifiers in depressed if they don't appear in any of the final masks
depressed |= ~(depressed | latched | locked) & xkbMask;
const xkb_state_component newState
= xkb_state_update_mask(xkb_state,
depressed,
latched,
locked,
0,
0,
(state >> 13) & 3); // bits 13 and 14 report the state keyboard group
if ((newState & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) {
//qWarning("TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)");
}
updateXKBStateFromState(xkb_state, state);
}
}
@ -1463,7 +1467,16 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
if (type == QEvent::KeyPress)
targetWindow->updateNetWmUserTime(time);
xcb_keysym_t sym = xkb_state_key_get_one_sym(xkb_state, code);
// Have a temporary keyboard state filled in from state
// this way we allow for synthetic events to have different state
// from the current state i.e. you can have Alt+Ctrl pressed
// and receive a synthetic key event that has neither Alt nor Ctrl pressed
struct xkb_state *kb_state = xkb_state_new(xkb_keymap);
if (!kb_state)
return;
updateXKBStateFromState(kb_state, state);
xcb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, code);
QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
QMetaMethod method;
@ -1482,11 +1495,13 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
Q_ARG(uint, code),
Q_ARG(uint, state),
Q_ARG(bool, type == QEvent::KeyPress));
if (retval)
if (retval) {
xkb_state_unref(kb_state);
return;
}
}
QString string = lookupString(xkb_state, code);
QString string = lookupString(kb_state, code);
// Ιf control modifier is set we should prefer latin character, this is
// used for standard shortcuts in checks like "key == QKeySequence::Copy",
@ -1557,6 +1572,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
QWindowSystemInterface::handleExtendedKeyEvent(window, time, QEvent::KeyPress, qtcode, modifiers,
code, sym, state, string, isAutoRepeat);
}
xkb_state_unref(kb_state);
}
QString QXcbKeyboard::lookupString(struct xkb_state *state, xcb_keycode_t code) const

View File

@ -104,6 +104,8 @@ protected:
void checkForLatinLayout();
private:
void updateXKBStateFromState(struct xkb_state *kb_state, quint16 state);
bool m_config;
xcb_keycode_t m_autorepeat_code;

View File

@ -459,6 +459,24 @@ void tst_QTextDocument::findMultiple()
cursor = doc->find(expr, cursor);
QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
QRegularExpression regularExpression("bar");
cursor.movePosition(QTextCursor::End);
cursor = doc->find(regularExpression, cursor, QTextDocument::FindBackward);
QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
cursor = doc->find(regularExpression, cursor, QTextDocument::FindBackward);
QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
cursor.movePosition(QTextCursor::Start);
cursor = doc->find(regularExpression, cursor);
QCOMPARE(cursor.selectionStart(), text.indexOf("bar"));
QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
cursor = doc->find(regularExpression, cursor);
QCOMPARE(cursor.selectionStart(), text.lastIndexOf("bar"));
QCOMPARE(cursor.selectionEnd(), cursor.selectionStart() + 3);
}
void tst_QTextDocument::basicIsModifiedChecks()

View File

@ -26,14 +26,7 @@
**
****************************************************************************/
#include <QGuiApplication>
#include <QOpenGLWindow>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QPainter>
#include <QElapsedTimer>
#include <QCommandLineParser>
#include <QScreen>
#include <QtGui>
const char applicationDescription[] = "\n\
This application opens multiple windows and continuously schedules updates for\n\
@ -62,67 +55,95 @@ class Window : public QOpenGLWindow
{
Q_OBJECT
public:
Window(int n) : idx(n) {
r = g = b = fps = 0;
y = 0;
Window(int index) : windowNumber(index + 1), x(0), framesSwapped(0) {
color = QColor::fromHsl((index * 30) % 360, 255, 127).toRgb();
resize(200, 200);
setObjectName(QString("Window %1").arg(windowNumber));
connect(this, SIGNAL(frameSwapped()), SLOT(frameSwapped()));
fpsTimer.start();
}
void paintGL() {
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glClearColor(r, g, b, 1);
f->glClearColor(color.redF(), color.greenF(), color.blueF(), 1);
f->glClear(GL_COLOR_BUFFER_BIT);
switch (idx % 3) {
case 0:
r += 0.005f;
break;
case 1:
g += 0.005f;
break;
case 2:
b += 0.005f;
break;
}
if (r > 1)
r = 0;
if (g > 1)
g = 0;
if (b > 1)
b = 0;
QPainter p(this);
p.setPen(Qt::white);
p.drawText(QPoint(20, y), QString(QLatin1String("Window %1 (%2 FPS)")).arg(idx).arg(fps));
y += 1;
if (y > height() - 20)
y = 20;
update();
QPainter painter(this);
painter.drawLine(x, 0, x, height());
x = ++x % width();
}
public slots:
void frameSwapped() {
++framesSwapped;
if (fpsTimer.elapsed() > 1000) {
fps = qRound(framesSwapped * (1000.0 / fpsTimer.elapsed()));
framesSwapped = 0;
fpsTimer.restart();
}
update();
}
protected:
void exposeEvent(QExposeEvent *event) {
if (!isExposed())
return;
QSurfaceFormat format = context()->format();
qDebug() << this << format.swapBehavior() << "with Vsync =" << (format.swapInterval() ? "ON" : "OFF");
if (format.swapInterval() != requestedFormat().swapInterval())
qWarning() << "WARNING: Did not get requested swap interval of" << requestedFormat().swapInterval() << "for" << this;
QOpenGLWindow::exposeEvent(event);
}
void mousePressEvent(QMouseEvent *event) {
qDebug() << this << event;
color.setHsl((color.hue() + 90) % 360, color.saturation(), color.lightness());
color = color.toRgb();
}
private:
int idx;
GLfloat r, g, b;
int y;
int windowNumber;
QColor color;
int x;
int framesSwapped;
QElapsedTimer fpsTimer;
int fps;
friend void printFps();
};
static const qreal kFpsInterval = 500;
void printFps()
{
static QElapsedTimer timer;
if (!timer.isValid()) {
timer.start();
return;
}
const qreal frameFactor = (kFpsInterval / timer.elapsed()) * (1000.0 / kFpsInterval);
QDebug output = qDebug().nospace();
qreal averageFps = 0;
const QWindowList windows = QGuiApplication::topLevelWindows();
for (int i = 0; i < windows.size(); ++i) {
Window *w = qobject_cast<Window*>(windows.at(i));
Q_ASSERT(w);
int fps = qRound(w->framesSwapped * frameFactor);
output << (i + 1) << "=" << fps << ", ";
averageFps += fps;
w->framesSwapped = 0;
}
averageFps = qRound(averageFps / windows.size());
qreal msPerFrame = 1000.0 / averageFps;
output << "avg=" << averageFps << ", ms=" << msPerFrame;
timer.restart();
}
int main(int argc, char **argv)
{
QGuiApplication app(argc, argv);
@ -144,30 +165,24 @@ int main(int argc, char **argv)
parser.process(app);
QSurfaceFormat fmt;
if (parser.isSet(noVsyncOption)) {
qDebug("swap interval 0 (no throttling)");
fmt.setSwapInterval(0);
} else {
qDebug("swap interval 1 (sync to vblank)");
}
QSurfaceFormat::setDefaultFormat(fmt);
QSurfaceFormat defaultSurfaceFormat;
defaultSurfaceFormat.setSwapInterval(parser.isSet(noVsyncOption) ? 0 : 1);
QSurfaceFormat::setDefaultFormat(defaultSurfaceFormat);
QRect availableGeometry = app.primaryScreen()->availableGeometry();
int numberOfWindows = qMax(parser.value(numWindowsOption).toInt(), 1);
QList<QWindow *> windows;
for (int i = 0; i < numberOfWindows; ++i) {
Window *w = new Window(i + 1);
Window *w = new Window(i);
windows << w;
if (i == 0 && parser.isSet(vsyncOneOption)) {
qDebug("swap interval 1 for first window only");
QSurfaceFormat vsyncedSurfaceFormat = fmt;
QSurfaceFormat vsyncedSurfaceFormat = defaultSurfaceFormat;
vsyncedSurfaceFormat.setSwapInterval(1);
w->setFormat(vsyncedSurfaceFormat);
fmt.setSwapInterval(0);
QSurfaceFormat::setDefaultFormat(fmt);
defaultSurfaceFormat.setSwapInterval(0);
QSurfaceFormat::setDefaultFormat(defaultSurfaceFormat);
}
static int windowWidth = w->width() + 20;
@ -182,9 +197,15 @@ int main(int argc, char **argv)
QPoint position = availableGeometry.topLeft();
position += QPoint(col * windowWidth, row * windowHeight);
w->setFramePosition(position);
w->show();
w->showNormal();
}
QTimer fpsTimer;
fpsTimer.setInterval(kFpsInterval);
fpsTimer.setTimerType(Qt::PreciseTimer);
QObject::connect(&fpsTimer, &QTimer::timeout, &printFps);
fpsTimer.start();
int r = app.exec();
qDeleteAll(windows);
return r;