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
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"
......@@ -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".])
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++])
......@@ -11160,11 +11161,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)
......
......@@ -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 \
......
......@@ -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 \
......
......@@ -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()
......
......@@ -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()
......
......@@ -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()
......
......@@ -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
......
......@@ -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 >
......
/* -*- 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