summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJan-Marek Glogowski <glogow@fbihome.de>2014-03-10 15:05:22 +0000
committerCaolán McNamara <caolanm@redhat.com>2014-03-22 15:09:25 +0000
commitf13a93eef2b860ce14d036e6657a6ed8dbfe3651 (patch)
tree0e7f914ae45da5fc537b806ba71bc6ff9a15dc44
parent7f16f6b83cb0f4db3fd301304d55c599ff0a2edd (diff)
KDE4: add Qt4 glib ExcludeSocket runtime check
Add a runtime check and configure warning to disable KDE4 native file pickers, if the Qt4 glib dispatcher doesn't honor the QEventLoop::ExcludeSocketNotifiers flag. This way polling the QClipboard using the event loop won't crash LibreOffice with recursive paint events, See https://bugreports.qt-project.org/browse/QTBUG-37380 (cherry picked from commit cc8d566d74a2e0b969b92d9cf22cc95a3bf31a98) Change-Id: I48795deef82eaf6f95a4e518baea66c52f0b7400 Reviewed-on: https://gerrit.libreoffice.org/8708 Reviewed-by: Caolán McNamara <caolanm@redhat.com> Tested-by: Caolán McNamara <caolanm@redhat.com>
-rw-r--r--configure.ac51
-rw-r--r--vcl/CustomTarget_kde4_moc.mk3
-rw-r--r--vcl/Library_vclplug_kde4.mk2
-rw-r--r--vcl/unx/kde4/KDE4FilePicker.cxx8
-rw-r--r--vcl/unx/kde4/KDEData.cxx2
-rw-r--r--vcl/unx/kde4/KDESalInstance.cxx10
-rw-r--r--vcl/unx/kde4/KDEXLib.cxx30
-rw-r--r--vcl/unx/kde4/KDEXLib.hxx2
-rw-r--r--vcl/unx/kde4/tst_exclude_socket_notifiers.hxx126
9 files changed, 212 insertions, 22 deletions
diff --git a/configure.ac b/configure.ac
index 50f116224fda..f36cbaf747e2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11126,7 +11126,7 @@ if test "$test_kde4" = "yes" -a "$ENABLE_KDE4" = "TRUE"; then
fi
qt_test_include="Qt/qobject.h"
- qt_test_library="libQtCore.so"
+ qt_test_library="libQtNetwork.so"
kde_test_include="kwindowsystem.h"
kde_test_library="libsolid.so"
@@ -11213,8 +11213,9 @@ the root of your Qt installation by exporting QT4DIR before running "configure".
AC_MSG_ERROR([KDE4 libraries not found. Please specify the root of your KDE4 installation by exporting KDE4DIR before running "configure".])
fi
- KDE4_CFLAGS="`pkg-config --cflags QtCore` `pkg-config --cflags QtGui` -I$kde_incdir -DQT_CLEAN_NAMESPACE -DQT_THREAD_SUPPORT"
- KDE4_LIBS="-L$kde_libdir -L$qt_lib_dir -lkio -lkfile -lkdeui -lkdecore -lQtCore -lQtGui"
+ PKG_CHECK_MODULES([QT4],[QtNetwork QtGui])
+ KDE4_CFLAGS="-I$kde_incdir $QT4_CFLAGS -DQT_CLEAN_NAMESPACE -DQT_THREAD_SUPPORT"
+ KDE4_LIBS="-L$kde_libdir -lkio -lkfile -lkdeui -lkdecore -L$qt_lib_dir $QT4_LIBS"
KDE4_CFLAGS=$(printf '%s' "$KDE4_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g")
AC_LANG_PUSH([C++])
@@ -11236,11 +11237,47 @@ int main(int argc, char **argv) {
# Sets also KDE_GLIB_CFLAGS/KDE_GLIB_LIBS if successful.
PKG_CHECK_MODULES(KDE_GLIB,[glib-2.0 >= 2.4],
[
- KDE_HAVE_GLIB=1
- AC_DEFINE(KDE_HAVE_GLIB,1)
- KDE_GLIB_CFLAGS=$(printf '%s' "$KDE_GLIB_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g")
+ KDE_HAVE_GLIB=1
+ AC_DEFINE(KDE_HAVE_GLIB,1)
+ KDE_GLIB_CFLAGS=$(printf '%s' "$KDE_GLIB_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g")
+
+ AC_LANG_PUSH([C++])
+ save_CXXFLAGS=$CXXFLAGS
+ CXXFLAGS="$CXXFLAGS $KDE4_CFLAGS"
+ save_LIBS=$LIBS
+ LIBS="$LIBS $KDE4_LIBS"
+ AC_MSG_CHECKING([whether Qt has fixed ExcludeSocketNotifiers])
+
+ # Prepare meta object data
+ TSTBASE="tst_exclude_socket_notifiers"
+ TSTMOC="${SRC_ROOT}/vcl/unx/kde4/${TSTBASE}"
+ ln -fs "${TSTMOC}.hxx"
+ $MOC4 "${TSTBASE}.hxx" -o "${TSTBASE}.moc"
+
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#include "tst_exclude_socket_notifiers.moc"
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+ exit(tst_processEventsExcludeSocket());
+ return 0;
+}
+ ]])],[
+ AC_MSG_RESULT([yes])
+ ],[
+ AC_MSG_RESULT([no])
+ AC_MSG_WARN([native KDE4 file pickers will be disabled at runtime - fix your Qt4 library!])
+ ])
+
+ # Remove meta object data
+ rm -f "${TSTBASE}."*
+
+ LIBS=$save_LIBS
+ CXXFLAGS=$save_CXXFLAGS
+ AC_LANG_POP([C++])
],
- AC_MSG_WARN([[No Glib found, KDE4 support will not integrate with Qt's Glib event loop support]]))
+ AC_MSG_WARN([[No Glib found, KDE4 support will not use native file pickers!]]))
fi
AC_SUBST(KDE4_CFLAGS)
AC_SUBST(KDE4_LIBS)
diff --git a/vcl/CustomTarget_kde4_moc.mk b/vcl/CustomTarget_kde4_moc.mk
index 0846b0b7f261..9e417548da4b 100644
--- a/vcl/CustomTarget_kde4_moc.mk
+++ b/vcl/CustomTarget_kde4_moc.mk
@@ -11,7 +11,8 @@ $(eval $(call gb_CustomTarget_CustomTarget,vcl/unx/kde4))
$(call gb_CustomTarget_get_target,vcl/unx/kde4) : \
$(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/KDEXLib.moc \
- $(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/KDE4FilePicker.moc
+ $(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/KDE4FilePicker.moc \
+ $(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/tst_exclude_socket_notifiers.moc
$(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/%.moc : \
$(SRCDIR)/vcl/unx/kde4/%.hxx \
diff --git a/vcl/Library_vclplug_kde4.mk b/vcl/Library_vclplug_kde4.mk
index c72fe004d727..18ffa02dc183 100644
--- a/vcl/Library_vclplug_kde4.mk
+++ b/vcl/Library_vclplug_kde4.mk
@@ -76,7 +76,7 @@ endif
$(eval $(call gb_Library_add_exception_objects,vclplug_kde4,\
vcl/unx/kde4/KDEData \
- vcl/unx/kde4/KDE4FilePicker \
+ vcl/unx/kde4/KDE4FilePicker \
vcl/unx/kde4/KDESalDisplay \
vcl/unx/kde4/KDESalFrame \
vcl/unx/kde4/KDESalGraphics \
diff --git a/vcl/unx/kde4/KDE4FilePicker.cxx b/vcl/unx/kde4/KDE4FilePicker.cxx
index b3fde26b90a2..1cb146002236 100644
--- a/vcl/unx/kde4/KDE4FilePicker.cxx
+++ b/vcl/unx/kde4/KDE4FilePicker.cxx
@@ -261,8 +261,12 @@ sal_Int16 SAL_CALL KDE4FilePicker::execute()
_dialog->filterWidget()->setEditable(false);
// We're entering a nested loop.
- // Release the yield mutex to prevent deadlocks.
- int result = _dialog->exec();
+ int result;
+ {
+ // Release the yield mutex to prevent deadlocks.
+ SalYieldMutexReleaser aReleaser;
+ result = _dialog->exec();
+ }
// HACK: KFileDialog uses KConfig("kdeglobals") for saving some settings
// (such as the auto-extension flag), but that doesn't update KGlobal::config()
diff --git a/vcl/unx/kde4/KDEData.cxx b/vcl/unx/kde4/KDEData.cxx
index ccbbd9998320..0e87c6209cf6 100644
--- a/vcl/unx/kde4/KDEData.cxx
+++ b/vcl/unx/kde4/KDEData.cxx
@@ -27,6 +27,7 @@
#include "KDEData.hxx"
#include "KDEXLib.hxx"
+#include "KDESalDisplay.hxx"
KDEData::~KDEData()
@@ -37,6 +38,7 @@ void KDEData::Init()
{
pXLib_ = new KDEXLib();
pXLib_->Init();
+ SetDisplay( SalKDEDisplay::self() );
}
void KDEData::initNWF()
diff --git a/vcl/unx/kde4/KDESalInstance.cxx b/vcl/unx/kde4/KDESalInstance.cxx
index 9670172e216e..023d79051741 100644
--- a/vcl/unx/kde4/KDESalInstance.cxx
+++ b/vcl/unx/kde4/KDESalInstance.cxx
@@ -32,10 +32,14 @@ SalFrame* KDESalInstance::CreateFrame( SalFrame *pParent, sal_uLong nState )
}
uno::Reference< ui::dialogs::XFilePicker2 > KDESalInstance::createFilePicker(
- const uno::Reference< uno::XComponentContext >& xMSF )
+ const uno::Reference< uno::XComponentContext >& xMSF )
{
- return uno::Reference< ui::dialogs::XFilePicker2 >(
- static_cast<KDEXLib*>( mpXLib )->createFilePicker(xMSF) );
+ KDEXLib* kdeXLib = static_cast<KDEXLib*>( mpXLib );
+ if (kdeXLib->haveQt4SocketExcludeFix())
+ return uno::Reference< ui::dialogs::XFilePicker2 >(
+ kdeXLib->createFilePicker(xMSF) );
+ else
+ return X11SalInstance::createFilePicker( xMSF );
}
int KDESalInstance::getFrameWidth()
diff --git a/vcl/unx/kde4/KDEXLib.cxx b/vcl/unx/kde4/KDEXLib.cxx
index 5c4cd10f01e3..820d39aa661e 100644
--- a/vcl/unx/kde4/KDEXLib.cxx
+++ b/vcl/unx/kde4/KDEXLib.cxx
@@ -19,7 +19,6 @@
#include "VCLKDEApplication.hxx"
-#include "KDE4FilePicker.hxx"
#include "KDESalInstance.hxx"
#include <kapplication.h>
@@ -45,15 +44,17 @@
#include <config_kde4.h>
+#if KDE_HAVE_GLIB
+#include "KDE4FilePicker.hxx"
+#include "tst_exclude_socket_notifiers.moc"
+#endif
+
KDEXLib::KDEXLib() :
SalXLib(), m_bStartupDone(false), m_pApplication(0),
m_pFreeCmdLineArgs(0), m_pAppCmdLineArgs(0), m_nFakeCmdLineArgs( 0 ),
- m_frameWidth( -1 ), m_isGlibEventLoopType(false)
+ m_frameWidth( -1 ), m_isGlibEventLoopType(false),
+ m_haveQt4SocketExcludeFix(false)
{
-#if KDE_HAVE_GLIB
- m_isGlibEventLoopType = QAbstractEventDispatcher::instance()->inherits( "QEventDispatcherGlib" );
-#endif
-
// the timers created here means they belong to the main thread.
// As the timeoutTimer runs the LO event queue, which may block on a dialog,
// the timer has to use a Qt::QueuedConnection, otherwise the nested event
@@ -168,6 +169,14 @@ void KDEXLib::Init()
m_pApplication = new VCLKDEApplication();
kapp->disableSessionManagement();
KApplication::setQuitOnLastWindowClosed(false);
+
+#if KDE_HAVE_GLIB
+ m_isGlibEventLoopType = QAbstractEventDispatcher::instance()->inherits( "QEventDispatcherGlib" );
+ if (m_isGlibEventLoopType && (0 == tst_processEventsExcludeSocket()))
+ // See http://bugreports.qt.nokia.com/browse/QTBUG-37380
+ m_haveQt4SocketExcludeFix = true;
+#endif
+
setupEventLoop();
Display* pDisp = QX11Info::display();
@@ -188,9 +197,8 @@ void KDEXLib::Init()
#include <glib.h>
static GPollFunc old_gpoll = NULL;
-static gint gpoll_wrapper( GPollFD*, guint, gint );
-gint gpoll_wrapper( GPollFD* ufds, guint nfds, gint timeout )
+static gint gpoll_wrapper( GPollFD* ufds, guint nfds, gint timeout )
{
SalYieldMutexReleaser release; // release YieldMutex (and re-acquire at block end)
return old_gpoll( ufds, nfds, timeout );
@@ -215,6 +223,8 @@ void KDEXLib::setupEventLoop()
{
old_gpoll = g_main_context_get_poll_func( NULL );
g_main_context_set_poll_func( NULL, gpoll_wrapper );
+ if( m_haveQt4SocketExcludeFix )
+ m_pApplication->clipboard()->setProperty( "useEventLoopWhenWaiting", true );
return;
}
#endif
@@ -367,11 +377,15 @@ using namespace com::sun::star;
uno::Reference< ui::dialogs::XFilePicker2 > KDEXLib::createFilePicker(
const uno::Reference< uno::XComponentContext >& xMSF )
{
+#if KDE_HAVE_GLIB
if( qApp->thread() != QThread::currentThread()) {
SalYieldMutexReleaser aReleaser;
return Q_EMIT createFilePickerSignal( xMSF );
}
return uno::Reference< ui::dialogs::XFilePicker2 >( new KDE4FilePicker( xMSF ) );
+#else
+ return NULL;
+#endif
}
#define Region QtXRegion
diff --git a/vcl/unx/kde4/KDEXLib.hxx b/vcl/unx/kde4/KDEXLib.hxx
index f26948d48e14..a88258c5985e 100644
--- a/vcl/unx/kde4/KDEXLib.hxx
+++ b/vcl/unx/kde4/KDEXLib.hxx
@@ -53,6 +53,7 @@ class KDEXLib : public QObject, public SalXLib
QTimer userEventTimer;
int m_frameWidth;
bool m_isGlibEventLoopType;
+ bool m_haveQt4SocketExcludeFix;
private:
void setupEventLoop();
@@ -87,6 +88,7 @@ class KDEXLib : public QObject, public SalXLib
virtual void PostUserEvent();
void doStartup();
+ bool haveQt4SocketExcludeFix() { return m_haveQt4SocketExcludeFix; }
public Q_SLOTS:
com::sun::star::uno::Reference< com::sun::star::ui::dialogs::XFilePicker2 >
diff --git a/vcl/unx/kde4/tst_exclude_socket_notifiers.hxx b/vcl/unx/kde4/tst_exclude_socket_notifiers.hxx
new file mode 100644
index 000000000000..0c874fd7147d
--- /dev/null
+++ b/vcl/unx/kde4/tst_exclude_socket_notifiers.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ *
+ * This code is based on the SocketEventsTester from the Qt4 test suite.
+ */
+
+#pragma once
+
+#include <qcoreapplication.h>
+#include <qeventloop.h>
+#include <qthread.h>
+#include <qtimer.h>
+#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qtcpsocket.h>
+
+class SocketEventsTester: public QObject
+{
+ Q_OBJECT
+public:
+ SocketEventsTester()
+ {
+ socket = 0;
+ server = 0;
+ dataSent = false;
+ testResult = false;
+ dataArrived = false;
+ }
+ ~SocketEventsTester()
+ {
+ delete socket;
+ delete server;
+ }
+ bool init()
+ {
+ bool ret = false;
+ server = new QTcpServer();
+ socket = new QTcpSocket();
+ connect(server, SIGNAL(newConnection()), this, SLOT(sendHello()));
+ connect(socket, SIGNAL(readyRead()), this, SLOT(sendAck()), Qt::DirectConnection);
+ if((ret = server->listen(QHostAddress::LocalHost, 0))) {
+ socket->connectToHost(server->serverAddress(), server->serverPort());
+ socket->waitForConnected();
+ }
+ return ret;
+ }
+
+ QTcpSocket *socket;
+ QTcpServer *server;
+ bool dataSent;
+ bool testResult;
+ bool dataArrived;
+public slots:
+ void sendAck()
+ {
+ dataArrived = true;
+ }
+ void sendHello()
+ {
+ char data[10] ="HELLO";
+ qint64 size = sizeof(data);
+
+ QTcpSocket *serverSocket = server->nextPendingConnection();
+ serverSocket->write(data, size);
+ dataSent = serverSocket->waitForBytesWritten(-1);
+ QEventLoop loop;
+ //allow the TCP/IP stack time to loopback the data, so our socket is ready to read
+ QTimer::singleShot(200, &loop, SLOT(quit()));
+ loop.exec(QEventLoop::ExcludeSocketNotifiers);
+ testResult = dataArrived;
+ //check the deferred event is processed
+ QTimer::singleShot(200, &loop, SLOT(quit()));
+ loop.exec();
+ serverSocket->close();
+ QThread::currentThread()->exit(0);
+ }
+};
+
+class SocketTestThread : public QThread
+{
+ Q_OBJECT
+public:
+ SocketTestThread():QThread(0),testResult(false){};
+ void run()
+ {
+ SocketEventsTester *tester = new SocketEventsTester();
+ if (tester->init())
+ exec();
+ dataSent = tester->dataSent;
+ testResult = tester->testResult;
+ dataArrived = tester->dataArrived;
+ delete tester;
+ }
+ bool dataSent;
+ bool testResult;
+ bool dataArrived;
+};
+
+#define QVERIFY(a) \
+ if (!a) return 1;
+
+static int tst_processEventsExcludeSocket()
+{
+ SocketTestThread thread;
+ thread.start();
+ QVERIFY(thread.wait());
+ QVERIFY(thread.dataSent);
+ QVERIFY(!thread.testResult);
+ QVERIFY(thread.dataArrived);
+ return 0;
+}
+