Kaydet (Commit) cc8d566d authored tarafından Jan-Marek Glogowski's avatar Jan-Marek Glogowski

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

Change-Id: I5cad30ead74571e49a075c084cca7a19acff7523
üst 516a8ded
...@@ -11050,7 +11050,7 @@ if test "$test_kde4" = "yes" -a "$ENABLE_KDE4" = "TRUE"; then ...@@ -11050,7 +11050,7 @@ if test "$test_kde4" = "yes" -a "$ENABLE_KDE4" = "TRUE"; then
fi fi
qt_test_include="Qt/qobject.h" qt_test_include="Qt/qobject.h"
qt_test_library="libQtCore.so" qt_test_library="libQtNetwork.so"
kde_test_include="kwindowsystem.h" kde_test_include="kwindowsystem.h"
kde_test_library="libsolid.so" kde_test_library="libsolid.so"
...@@ -11137,8 +11137,9 @@ the root of your Qt installation by exporting QT4DIR before running "configure". ...@@ -11137,8 +11137,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".]) AC_MSG_ERROR([KDE4 libraries not found. Please specify the root of your KDE4 installation by exporting KDE4DIR before running "configure".])
fi fi
KDE4_CFLAGS="`pkg-config --cflags QtCore` `pkg-config --cflags QtGui` -I$kde_incdir -DQT_CLEAN_NAMESPACE -DQT_THREAD_SUPPORT" PKG_CHECK_MODULES([QT4],[QtNetwork QtGui])
KDE4_LIBS="-L$kde_libdir -L$qt_lib_dir -lkio -lkfile -lkdeui -lkdecore -lQtCore -lQtGui" 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") KDE4_CFLAGS=$(printf '%s' "$KDE4_CFLAGS" | sed -e "s/-I/${ISYSTEM?}/g")
AC_LANG_PUSH([C++]) AC_LANG_PUSH([C++])
...@@ -11160,11 +11161,47 @@ int main(int argc, char **argv) { ...@@ -11160,11 +11161,47 @@ int main(int argc, char **argv) {
# Sets also KDE_GLIB_CFLAGS/KDE_GLIB_LIBS if successful. # Sets also KDE_GLIB_CFLAGS/KDE_GLIB_LIBS if successful.
PKG_CHECK_MODULES(KDE_GLIB,[glib-2.0 >= 2.4], PKG_CHECK_MODULES(KDE_GLIB,[glib-2.0 >= 2.4],
[ [
KDE_HAVE_GLIB=1 KDE_HAVE_GLIB=1
AC_DEFINE(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_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 fi
AC_SUBST(KDE4_CFLAGS) AC_SUBST(KDE4_CFLAGS)
AC_SUBST(KDE4_LIBS) AC_SUBST(KDE4_LIBS)
......
...@@ -11,7 +11,8 @@ $(eval $(call gb_CustomTarget_CustomTarget,vcl/unx/kde4)) ...@@ -11,7 +11,8 @@ $(eval $(call gb_CustomTarget_CustomTarget,vcl/unx/kde4))
$(call gb_CustomTarget_get_target,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)/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 : \ $(call gb_CustomTarget_get_workdir,vcl/unx/kde4)/%.moc : \
$(SRCDIR)/vcl/unx/kde4/%.hxx \ $(SRCDIR)/vcl/unx/kde4/%.hxx \
......
...@@ -76,7 +76,7 @@ endif ...@@ -76,7 +76,7 @@ endif
$(eval $(call gb_Library_add_exception_objects,vclplug_kde4,\ $(eval $(call gb_Library_add_exception_objects,vclplug_kde4,\
vcl/unx/kde4/KDEData \ vcl/unx/kde4/KDEData \
vcl/unx/kde4/KDE4FilePicker \ vcl/unx/kde4/KDE4FilePicker \
vcl/unx/kde4/KDESalDisplay \ vcl/unx/kde4/KDESalDisplay \
vcl/unx/kde4/KDESalFrame \ vcl/unx/kde4/KDESalFrame \
vcl/unx/kde4/KDESalGraphics \ vcl/unx/kde4/KDESalGraphics \
......
...@@ -261,8 +261,12 @@ sal_Int16 SAL_CALL KDE4FilePicker::execute() ...@@ -261,8 +261,12 @@ sal_Int16 SAL_CALL KDE4FilePicker::execute()
_dialog->filterWidget()->setEditable(false); _dialog->filterWidget()->setEditable(false);
// We're entering a nested loop. // We're entering a nested loop.
// Release the yield mutex to prevent deadlocks. int result;
int result = _dialog->exec(); {
// Release the yield mutex to prevent deadlocks.
SalYieldMutexReleaser aReleaser;
result = _dialog->exec();
}
// HACK: KFileDialog uses KConfig("kdeglobals") for saving some settings // HACK: KFileDialog uses KConfig("kdeglobals") for saving some settings
// (such as the auto-extension flag), but that doesn't update KGlobal::config() // (such as the auto-extension flag), but that doesn't update KGlobal::config()
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "KDEData.hxx" #include "KDEData.hxx"
#include "KDEXLib.hxx" #include "KDEXLib.hxx"
#include "KDESalDisplay.hxx"
KDEData::~KDEData() KDEData::~KDEData()
...@@ -37,6 +38,7 @@ void KDEData::Init() ...@@ -37,6 +38,7 @@ void KDEData::Init()
{ {
pXLib_ = new KDEXLib(); pXLib_ = new KDEXLib();
pXLib_->Init(); pXLib_->Init();
SetDisplay( SalKDEDisplay::self() );
} }
void KDEData::initNWF() void KDEData::initNWF()
......
...@@ -32,10 +32,14 @@ SalFrame* KDESalInstance::CreateFrame( SalFrame *pParent, sal_uLong nState ) ...@@ -32,10 +32,14 @@ SalFrame* KDESalInstance::CreateFrame( SalFrame *pParent, sal_uLong nState )
} }
uno::Reference< ui::dialogs::XFilePicker2 > KDESalInstance::createFilePicker( 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 >( KDEXLib* kdeXLib = static_cast<KDEXLib*>( mpXLib );
static_cast<KDEXLib*>( mpXLib )->createFilePicker(xMSF) ); if (kdeXLib->haveQt4SocketExcludeFix())
return uno::Reference< ui::dialogs::XFilePicker2 >(
kdeXLib->createFilePicker(xMSF) );
else
return X11SalInstance::createFilePicker( xMSF );
} }
int KDESalInstance::getFrameWidth() int KDESalInstance::getFrameWidth()
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include "VCLKDEApplication.hxx" #include "VCLKDEApplication.hxx"
#include "KDE4FilePicker.hxx"
#include "KDESalInstance.hxx" #include "KDESalInstance.hxx"
#include <kapplication.h> #include <kapplication.h>
...@@ -45,15 +44,17 @@ ...@@ -45,15 +44,17 @@
#include <config_kde4.h> #include <config_kde4.h>
#if KDE_HAVE_GLIB
#include "KDE4FilePicker.hxx"
#include "tst_exclude_socket_notifiers.moc"
#endif
KDEXLib::KDEXLib() : KDEXLib::KDEXLib() :
SalXLib(), m_bStartupDone(false), m_pApplication(0), SalXLib(), m_bStartupDone(false), m_pApplication(0),
m_pFreeCmdLineArgs(0), m_pAppCmdLineArgs(0), m_nFakeCmdLineArgs( 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. // 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, // 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 // the timer has to use a Qt::QueuedConnection, otherwise the nested event
...@@ -168,6 +169,14 @@ void KDEXLib::Init() ...@@ -168,6 +169,14 @@ void KDEXLib::Init()
m_pApplication = new VCLKDEApplication(); m_pApplication = new VCLKDEApplication();
kapp->disableSessionManagement(); kapp->disableSessionManagement();
KApplication::setQuitOnLastWindowClosed(false); 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(); setupEventLoop();
Display* pDisp = QX11Info::display(); Display* pDisp = QX11Info::display();
...@@ -188,9 +197,8 @@ void KDEXLib::Init() ...@@ -188,9 +197,8 @@ void KDEXLib::Init()
#include <glib.h> #include <glib.h>
static GPollFunc old_gpoll = NULL; 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) SalYieldMutexReleaser release; // release YieldMutex (and re-acquire at block end)
return old_gpoll( ufds, nfds, timeout ); return old_gpoll( ufds, nfds, timeout );
...@@ -215,6 +223,8 @@ void KDEXLib::setupEventLoop() ...@@ -215,6 +223,8 @@ void KDEXLib::setupEventLoop()
{ {
old_gpoll = g_main_context_get_poll_func( NULL ); old_gpoll = g_main_context_get_poll_func( NULL );
g_main_context_set_poll_func( NULL, gpoll_wrapper ); g_main_context_set_poll_func( NULL, gpoll_wrapper );
if( m_haveQt4SocketExcludeFix )
m_pApplication->clipboard()->setProperty( "useEventLoopWhenWaiting", true );
return; return;
} }
#endif #endif
...@@ -367,11 +377,15 @@ using namespace com::sun::star; ...@@ -367,11 +377,15 @@ using namespace com::sun::star;
uno::Reference< ui::dialogs::XFilePicker2 > KDEXLib::createFilePicker( uno::Reference< ui::dialogs::XFilePicker2 > KDEXLib::createFilePicker(
const uno::Reference< uno::XComponentContext >& xMSF ) const uno::Reference< uno::XComponentContext >& xMSF )
{ {
#if KDE_HAVE_GLIB
if( qApp->thread() != QThread::currentThread()) { if( qApp->thread() != QThread::currentThread()) {
SalYieldMutexReleaser aReleaser; SalYieldMutexReleaser aReleaser;
return Q_EMIT createFilePickerSignal( xMSF ); return Q_EMIT createFilePickerSignal( xMSF );
} }
return uno::Reference< ui::dialogs::XFilePicker2 >( new KDE4FilePicker( xMSF ) ); return uno::Reference< ui::dialogs::XFilePicker2 >( new KDE4FilePicker( xMSF ) );
#else
return NULL;
#endif
} }
#define Region QtXRegion #define Region QtXRegion
......
...@@ -53,6 +53,7 @@ class KDEXLib : public QObject, public SalXLib ...@@ -53,6 +53,7 @@ class KDEXLib : public QObject, public SalXLib
QTimer userEventTimer; QTimer userEventTimer;
int m_frameWidth; int m_frameWidth;
bool m_isGlibEventLoopType; bool m_isGlibEventLoopType;
bool m_haveQt4SocketExcludeFix;
private: private:
void setupEventLoop(); void setupEventLoop();
...@@ -87,6 +88,7 @@ class KDEXLib : public QObject, public SalXLib ...@@ -87,6 +88,7 @@ class KDEXLib : public QObject, public SalXLib
virtual void PostUserEvent(); virtual void PostUserEvent();
void doStartup(); void doStartup();
bool haveQt4SocketExcludeFix() { return m_haveQt4SocketExcludeFix; }
public Q_SLOTS: public Q_SLOTS:
com::sun::star::uno::Reference< com::sun::star::ui::dialogs::XFilePicker2 > com::sun::star::uno::Reference< com::sun::star::ui::dialogs::XFilePicker2 >
......
/* -*- 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment