mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 10:22:48 +00:00
kdeui: KSelectionOwner rewrite
natural selection - kill or be killed Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
d37ea70af9
commit
c2a98d4c5d
11 changed files with 203 additions and 925 deletions
|
@ -251,7 +251,6 @@ install(
|
||||||
KSaveFile
|
KSaveFile
|
||||||
KSelectAction
|
KSelectAction
|
||||||
KSelectionOwner
|
KSelectionOwner
|
||||||
KSelectionWatcher
|
|
||||||
KSelector
|
KSelector
|
||||||
KSeparator
|
KSeparator
|
||||||
KService
|
KService
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
#include "../kmanagerselection.h"
|
#include "../kselectionowner.h"
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
#include "../kmanagerselection.h"
|
|
|
@ -181,7 +181,7 @@ set(kdeui_LIB_SRCS
|
||||||
util/kcursor.cpp
|
util/kcursor.cpp
|
||||||
util/kguiitem.cpp
|
util/kguiitem.cpp
|
||||||
util/kkeyserver.cpp
|
util/kkeyserver.cpp
|
||||||
util/kmanagerselection.cpp
|
util/kselectionowner.cpp
|
||||||
util/knumvalidator.cpp
|
util/knumvalidator.cpp
|
||||||
util/kpassivepopup.cpp
|
util/kpassivepopup.cpp
|
||||||
util/kpassivepopupmessagehandler.cpp
|
util/kpassivepopupmessagehandler.cpp
|
||||||
|
@ -500,7 +500,7 @@ install(
|
||||||
util/kcursor.h
|
util/kcursor.h
|
||||||
util/kguiitem.h
|
util/kguiitem.h
|
||||||
util/kkeyserver.h
|
util/kkeyserver.h
|
||||||
util/kmanagerselection.h
|
util/kselectionowner.h
|
||||||
util/knumvalidator.h
|
util/knumvalidator.h
|
||||||
util/kpassivepopup.h
|
util/kpassivepopup.h
|
||||||
util/kpassivepopupmessagehandler.h
|
util/kpassivepopupmessagehandler.h
|
||||||
|
|
|
@ -143,9 +143,7 @@ if (Q_WS_X11)
|
||||||
fixx11h_test
|
fixx11h_test
|
||||||
fixx11h_test2
|
fixx11h_test2
|
||||||
kxerrorhandlertest
|
kxerrorhandlertest
|
||||||
kmanagerselectiontest
|
|
||||||
)
|
)
|
||||||
target_link_libraries(kdeui-kmanagerselectiontest ${X11_X11_LIB})
|
|
||||||
target_link_libraries(kdeui-kxerrorhandlertest ${X11_X11_LIB})
|
target_link_libraries(kdeui-kxerrorhandlertest ${X11_X11_LIB})
|
||||||
endif (Q_WS_X11)
|
endif (Q_WS_X11)
|
||||||
|
|
||||||
|
|
|
@ -1,167 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of the KDE Libraries
|
|
||||||
|
|
||||||
Copyright (C) 2009 Lubos Lunak <l.lunak@kde.org>
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public License
|
|
||||||
along with this library; see the file COPYING.LIB. If not, write to
|
|
||||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
#include "kmanagerselectiontest.h"
|
|
||||||
|
|
||||||
#include <QtTest>
|
|
||||||
|
|
||||||
#include <kapplication.h>
|
|
||||||
#include <kmanagerselection.h>
|
|
||||||
#include <qx11info_x11.h>
|
|
||||||
|
|
||||||
#define SNAME "_KDE_KMANAGERSELECTIONTEST"
|
|
||||||
|
|
||||||
using namespace QTest;
|
|
||||||
|
|
||||||
void KManagerSelectionTest::testAcquireRelease()
|
|
||||||
{ // test that newOwner() is emitted when there is a new selection owner
|
|
||||||
KSelectionWatcher watcher( SNAME );
|
|
||||||
KSelectionOwner owner( SNAME );
|
|
||||||
QVERIFY( owner.ownerWindow() == None );
|
|
||||||
QVERIFY( watcher.owner() == None );
|
|
||||||
SigCheckWatcher sw( watcher );
|
|
||||||
SigCheckOwner so( owner );
|
|
||||||
QVERIFY( owner.claim( false ));
|
|
||||||
QVERIFY( kWaitForSignal( &watcher, SIGNAL(newOwner(Window)), 2000 ));
|
|
||||||
QVERIFY( sw.newowner == true );
|
|
||||||
QVERIFY( sw.lostowner == false );
|
|
||||||
QVERIFY( so.lostownership == false );
|
|
||||||
}
|
|
||||||
|
|
||||||
void KManagerSelectionTest::testInitiallyOwned()
|
|
||||||
{ // test that lostOwner() is emitted when the selection is disowned
|
|
||||||
KSelectionOwner owner( SNAME );
|
|
||||||
SigCheckOwner so( owner );
|
|
||||||
QVERIFY( owner.claim( false ));
|
|
||||||
KSelectionWatcher watcher( SNAME );
|
|
||||||
SigCheckWatcher sw( watcher );
|
|
||||||
owner.release();
|
|
||||||
QVERIFY( kWaitForSignal( &watcher, SIGNAL(lostOwner()), 2000 ));
|
|
||||||
QVERIFY( sw.newowner == false );
|
|
||||||
QVERIFY( sw.lostowner == true );
|
|
||||||
QVERIFY( so.lostownership == false );
|
|
||||||
}
|
|
||||||
|
|
||||||
void KManagerSelectionTest::testLostOwnership()
|
|
||||||
{ // test that lostOwnership() is emitted when something else forces taking the ownership
|
|
||||||
KSelectionOwner owner1( SNAME );
|
|
||||||
KSelectionOwner owner2( SNAME );
|
|
||||||
QVERIFY( owner1.claim( false ));
|
|
||||||
QVERIFY( !owner2.claim( false ));
|
|
||||||
XEvent ev;
|
|
||||||
ev.xselectionclear.type = SelectionClear;
|
|
||||||
ev.xselectionclear.serial = XLastKnownRequestProcessed( QX11Info::display());
|
|
||||||
ev.xselectionclear.send_event = True;
|
|
||||||
ev.xselectionclear.display = QX11Info::display();
|
|
||||||
ev.xselectionclear.window = owner1.ownerWindow();
|
|
||||||
ev.xselectionclear.selection = XInternAtom( QX11Info::display(), SNAME, False );
|
|
||||||
ev.xselectionclear.time = QX11Info::appTime();
|
|
||||||
QVERIFY( owner2.claim( true, false ));
|
|
||||||
// SelectionClear event is not sent to the same X client, so fake it
|
|
||||||
XPutBackEvent( QX11Info::display(), &ev );
|
|
||||||
QVERIFY( kWaitForSignal( &owner1, SIGNAL(lostOwnership()), 2000 ));
|
|
||||||
QVERIFY( owner1.ownerWindow() == None );
|
|
||||||
QVERIFY( owner2.ownerWindow() != None );
|
|
||||||
}
|
|
||||||
|
|
||||||
void KManagerSelectionTest::testWatching()
|
|
||||||
{ // test that KSelectionWatcher reports changes properly
|
|
||||||
KSelectionWatcher watcher( SNAME );
|
|
||||||
KSelectionOwner owner1( SNAME );
|
|
||||||
KSelectionOwner owner2( SNAME );
|
|
||||||
SigCheckWatcher sw( watcher );
|
|
||||||
QVERIFY( owner1.claim( false ));
|
|
||||||
QVERIFY( kWaitForSignal( &watcher, SIGNAL(newOwner(Window)), 2000 ));
|
|
||||||
QVERIFY( sw.newowner == true );
|
|
||||||
QVERIFY( sw.lostowner == false );
|
|
||||||
sw.newowner = sw.lostowner = false;
|
|
||||||
QVERIFY( owner2.claim( true, false ));
|
|
||||||
QVERIFY( kWaitForSignal( &watcher, SIGNAL(newOwner(Window)), 2000 ));
|
|
||||||
QVERIFY( sw.newowner == true );
|
|
||||||
QVERIFY( sw.lostowner == false );
|
|
||||||
sw.newowner = sw.lostowner = false;
|
|
||||||
owner2.release();
|
|
||||||
QVERIFY( kWaitForSignal( &watcher, SIGNAL(lostOwner()), 2000 ));
|
|
||||||
QVERIFY( sw.newowner == false );
|
|
||||||
QVERIFY( sw.lostowner == true );
|
|
||||||
sw.newowner = sw.lostowner = false;
|
|
||||||
QVERIFY( owner2.claim( false ));
|
|
||||||
QVERIFY( kWaitForSignal( &watcher, SIGNAL(newOwner(Window)), 2000 ));
|
|
||||||
QVERIFY( sw.newowner == true );
|
|
||||||
QVERIFY( sw.lostowner == false );
|
|
||||||
}
|
|
||||||
|
|
||||||
SigCheckOwner::SigCheckOwner( const KSelectionOwner& owner )
|
|
||||||
: lostownership( false )
|
|
||||||
{
|
|
||||||
connect( &owner, SIGNAL(lostOwnership()), this, SLOT(lostOwnership()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SigCheckOwner::lostOwnership()
|
|
||||||
{
|
|
||||||
lostownership = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
SigCheckWatcher::SigCheckWatcher( const KSelectionWatcher& watcher )
|
|
||||||
: newowner( false )
|
|
||||||
, lostowner( false )
|
|
||||||
{
|
|
||||||
connect( &watcher, SIGNAL(newOwner(Window)), this, SLOT(newOwner()));
|
|
||||||
connect( &watcher, SIGNAL(lostOwner()), this, SLOT(lostOwner()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SigCheckWatcher::newOwner()
|
|
||||||
{
|
|
||||||
newowner = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SigCheckWatcher::lostOwner()
|
|
||||||
{
|
|
||||||
lostowner = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#include <kapplication.h>
|
|
||||||
|
|
||||||
// the tested classes need KApplication - this is from qtest_kde.h, with QApp -> KApp
|
|
||||||
#define QTEST_KDEMAIN_WITH_COMPONENTNAME_KAPP(TestObject, flags, componentName) \
|
|
||||||
int main(int argc, char *argv[]) \
|
|
||||||
{ \
|
|
||||||
setenv("LC_ALL", "C", 1); \
|
|
||||||
assert( !QDir::homePath().isEmpty() ); \
|
|
||||||
setenv("KDEHOME", QFile::encodeName( QDir::homePath() + QLatin1String("/.kde-unit-test") ), 1); \
|
|
||||||
setenv("XDG_DATA_HOME", QFile::encodeName( QDir::homePath() + QLatin1String("/.kde-unit-test/xdg/local") ), 1); \
|
|
||||||
setenv("XDG_CONFIG_HOME", QFile::encodeName( QDir::homePath() + QLatin1String("/.kde-unit-test/xdg/config") ), 1); \
|
|
||||||
unsetenv("KDE_DEBUG_COLOR"); \
|
|
||||||
QFile::remove(QDir::homePath() + QLatin1String("/.kde-unit-test/share/config/qttestrc")); \
|
|
||||||
KAboutData aboutData( QByteArray(componentName), QByteArray(), ki18n("KDE Test Program"), QByteArray("version") ); \
|
|
||||||
KCmdLineArgs::init( argc, argv, &aboutData); \
|
|
||||||
KApplication app; \
|
|
||||||
app.setApplicationName( QLatin1String("qttest") ); \
|
|
||||||
qRegisterMetaType<KUrl>(); /*as done by kapplication*/ \
|
|
||||||
qRegisterMetaType<KUrl::List>(); \
|
|
||||||
TestObject tc; \
|
|
||||||
KGlobal::ref(); /* don't quit qeventloop after closing a mainwindow */ \
|
|
||||||
return QTest::qExec( &tc, argc, argv ); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define QTEST_KDEMAIN_KAPP(TestObject, flags) QTEST_KDEMAIN_WITH_COMPONENTNAME_KAPP(TestObject, flags, "qttest")
|
|
||||||
|
|
||||||
QTEST_KDEMAIN_KAPP(KManagerSelectionTest, GUI)
|
|
|
@ -1,71 +0,0 @@
|
||||||
/*
|
|
||||||
This file is part of the KDE Libraries
|
|
||||||
|
|
||||||
Copyright (C) 2009 Lubos Lunak <l.lunak@kde.org>
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public License
|
|
||||||
along with this library; see the file COPYING.LIB. If not, write to
|
|
||||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
||||||
Boston, MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
#ifndef KMANAGERSELECTIONTESTTEST_H
|
|
||||||
#define KMANAGERSELECTIONTESTTEST_H
|
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include "qtest_kde.h"
|
|
||||||
|
|
||||||
class KManagerSelectionTest
|
|
||||||
: public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
private Q_SLOTS:
|
|
||||||
void testAcquireRelease();
|
|
||||||
void testInitiallyOwned();
|
|
||||||
void testLostOwnership();
|
|
||||||
void testWatching();
|
|
||||||
};
|
|
||||||
|
|
||||||
class KSelectionOwner;
|
|
||||||
class KSelectionWatcher;
|
|
||||||
|
|
||||||
// For checking whether several signal have or have not been emitted,
|
|
||||||
// kWaitForSignal() is not powerful enough for that (it may be still
|
|
||||||
// needed to do the event processing though).
|
|
||||||
class SigCheckOwner
|
|
||||||
: public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
SigCheckOwner( const KSelectionOwner& owner );
|
|
||||||
private Q_SLOTS:
|
|
||||||
void lostOwnership();
|
|
||||||
public:
|
|
||||||
bool lostownership;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SigCheckWatcher
|
|
||||||
: public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
SigCheckWatcher( const KSelectionWatcher& watcher );
|
|
||||||
private Q_SLOTS:
|
|
||||||
void newOwner();
|
|
||||||
void lostOwner();
|
|
||||||
public:
|
|
||||||
bool newowner;
|
|
||||||
bool lostowner;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,468 +0,0 @@
|
||||||
/****************************************************************************
|
|
||||||
|
|
||||||
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
copy of this software and associated documentation files (the "Software"),
|
|
||||||
to deal in the Software without restriction, including without limitation
|
|
||||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#include "kmanagerselection.h"
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
#include <qx11info_x11.h>
|
|
||||||
#include <qwidget.h>
|
|
||||||
#include <kdebug.h>
|
|
||||||
#include <kapplication.h>
|
|
||||||
#include <kxerrorhandler.h>
|
|
||||||
#include <X11/Xatom.h>
|
|
||||||
|
|
||||||
|
|
||||||
class KSelectionOwner::Private : public QWidget
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Private( KSelectionOwner* owner_P, Atom selection_P, int screen_P )
|
|
||||||
: selection( selection_P ),
|
|
||||||
screen( screen_P >= 0 ? screen_P : DefaultScreen( QX11Info::display() ) ),
|
|
||||||
window( None ),
|
|
||||||
timestamp( CurrentTime ),
|
|
||||||
extra1( 0 ),
|
|
||||||
extra2( 0 ),
|
|
||||||
owner( owner_P )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const Atom selection;
|
|
||||||
const int screen;
|
|
||||||
Window window;
|
|
||||||
Time timestamp;
|
|
||||||
long extra1, extra2;
|
|
||||||
static Atom manager_atom;
|
|
||||||
static Atom xa_multiple;
|
|
||||||
static Atom xa_targets;
|
|
||||||
static Atom xa_timestamp;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool x11Event( XEvent* ev_P )
|
|
||||||
{
|
|
||||||
return owner->filterEvent( ev_P );
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
KSelectionOwner* owner;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
KSelectionOwner::KSelectionOwner( Atom selection_P, int screen_P, QObject* parent_P )
|
|
||||||
: QObject( parent_P ),
|
|
||||||
d( new Private( this, selection_P, screen_P ) )
|
|
||||||
{
|
|
||||||
kapp->installX11EventFilter( d );
|
|
||||||
}
|
|
||||||
|
|
||||||
KSelectionOwner::KSelectionOwner( const char* selection_P, int screen_P, QObject* parent_P )
|
|
||||||
: QObject( parent_P ),
|
|
||||||
d( new Private( this, XInternAtom( QX11Info::display(), selection_P, False ), screen_P ) )
|
|
||||||
{
|
|
||||||
kapp->installX11EventFilter( d );
|
|
||||||
}
|
|
||||||
|
|
||||||
KSelectionOwner::~KSelectionOwner()
|
|
||||||
{
|
|
||||||
if (kapp) {
|
|
||||||
kapp->removeX11EventFilter( d );
|
|
||||||
}
|
|
||||||
release();
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KSelectionOwner::claim( bool force_P, bool force_kill_P )
|
|
||||||
{
|
|
||||||
if ( Private::manager_atom == None ) {
|
|
||||||
getAtoms();
|
|
||||||
}
|
|
||||||
if ( d->timestamp != CurrentTime ) {
|
|
||||||
release();
|
|
||||||
}
|
|
||||||
Display* const dpy = QX11Info::display();
|
|
||||||
Window prev_owner = XGetSelectionOwner( dpy, d->selection );
|
|
||||||
if ( prev_owner != None ) {
|
|
||||||
if ( !force_P ) {
|
|
||||||
// kDebug() << "Selection already owned, failing";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
XSelectInput( dpy, prev_owner, StructureNotifyMask );
|
|
||||||
}
|
|
||||||
XSetWindowAttributes attrs;
|
|
||||||
attrs.override_redirect = True;
|
|
||||||
d->window = XCreateWindow( dpy, RootWindow( dpy, d->screen ), 0, 0, 1, 1,
|
|
||||||
0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &attrs );
|
|
||||||
// kDebug() << "Using owner window " << window;
|
|
||||||
Atom tmp = XA_ATOM;
|
|
||||||
XSelectInput( dpy, d->window, PropertyChangeMask );
|
|
||||||
XChangeProperty( dpy, d->window, XA_ATOM, XA_ATOM, 32, PropModeReplace,
|
|
||||||
reinterpret_cast< unsigned char* >( &tmp ), 1 );
|
|
||||||
XEvent ev;
|
|
||||||
XSync( dpy, False );
|
|
||||||
XCheckTypedWindowEvent( dpy, d->window, PropertyNotify, &ev ); // get a timestamp
|
|
||||||
d->timestamp = ev.xproperty.time;
|
|
||||||
XSelectInput( dpy, d->window, StructureNotifyMask ); // for DestroyNotify
|
|
||||||
XSetSelectionOwner( dpy, d->selection, d->window, d->timestamp );
|
|
||||||
Window new_owner = XGetSelectionOwner( dpy, d->selection );
|
|
||||||
if ( new_owner != d->window ) {
|
|
||||||
// kDebug() << "Failed to claim selection : " << new_owner;
|
|
||||||
XDestroyWindow( dpy, d->window );
|
|
||||||
d->timestamp = CurrentTime;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( prev_owner != None ) {
|
|
||||||
// kDebug() << "Waiting for previous owner to disown";
|
|
||||||
for ( int cnt = 0; ; ++cnt ) {
|
|
||||||
if ( XCheckTypedWindowEvent( dpy, prev_owner, DestroyNotify, &ev ) == True ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
struct timeval tm = { 0, 50000 }; // 50 ms
|
|
||||||
select( 0, NULL, NULL, NULL, &tm );
|
|
||||||
if ( cnt == 19 ) {
|
|
||||||
if ( force_kill_P ) {
|
|
||||||
KXErrorHandler err;
|
|
||||||
// kDebug() << "Killing previous owner";
|
|
||||||
XKillClient( dpy, prev_owner );
|
|
||||||
err.error( true ); // ignore errors when killing
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ev.type = ClientMessage;
|
|
||||||
ev.xclient.window = RootWindow( dpy, d->screen );
|
|
||||||
ev.xclient.display = dpy;
|
|
||||||
ev.xclient.message_type = Private::manager_atom;
|
|
||||||
ev.xclient.format = 32;
|
|
||||||
ev.xclient.data.l[ 0 ] = d->timestamp;
|
|
||||||
ev.xclient.data.l[ 1 ] = d->selection;
|
|
||||||
ev.xclient.data.l[ 2 ] = d->window;
|
|
||||||
ev.xclient.data.l[ 3 ] = d->extra1;
|
|
||||||
ev.xclient.data.l[ 4 ] = d->extra2;
|
|
||||||
XSendEvent( dpy, RootWindow( dpy, d->screen ), False, StructureNotifyMask, &ev );
|
|
||||||
// kDebug() << "Claimed selection";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// destroy resource first
|
|
||||||
void KSelectionOwner::release()
|
|
||||||
{
|
|
||||||
if ( d->timestamp == CurrentTime ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
XDestroyWindow( QX11Info::display(), d->window ); // also makes the selection not owned
|
|
||||||
// kDebug() << "Releasing selection";
|
|
||||||
d->timestamp = CurrentTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
Window KSelectionOwner::ownerWindow() const
|
|
||||||
{
|
|
||||||
if ( d->timestamp == CurrentTime ) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
return d->window;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KSelectionOwner::setData( long extra1_P, long extra2_P )
|
|
||||||
{
|
|
||||||
d->extra1 = extra1_P;
|
|
||||||
d->extra2 = extra2_P;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KSelectionOwner::filterEvent( XEvent* ev_P )
|
|
||||||
{
|
|
||||||
if ( d->timestamp != CurrentTime && ev_P->xany.window == d->window ) {
|
|
||||||
if ( handleMessage( ev_P )) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch( ev_P->type ) {
|
|
||||||
case SelectionClear: {
|
|
||||||
if ( d->timestamp == CurrentTime || ev_P->xselectionclear.selection != d->selection ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
d->timestamp = CurrentTime;
|
|
||||||
// kDebug() << "Lost selection";
|
|
||||||
Window window = d->window;
|
|
||||||
emit lostOwnership();
|
|
||||||
XSelectInput( QX11Info::display(), window, 0 );
|
|
||||||
XDestroyWindow( QX11Info::display(), window );
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case DestroyNotify: {
|
|
||||||
if ( d->timestamp == CurrentTime || ev_P->xdestroywindow.window != d->window ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
d->timestamp = CurrentTime;
|
|
||||||
// kDebug() << "Lost selection (destroyed)";
|
|
||||||
emit lostOwnership();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case SelectionNotify: {
|
|
||||||
if ( d->timestamp == CurrentTime || ev_P->xselection.selection != d->selection ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// ignore?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
case SelectionRequest: {
|
|
||||||
filter_selection_request( ev_P->xselectionrequest );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KSelectionOwner::handleMessage( XEvent* )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KSelectionOwner::filter_selection_request( XSelectionRequestEvent& ev_P )
|
|
||||||
{
|
|
||||||
if ( d->timestamp == CurrentTime || ev_P.selection != d->selection ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( ev_P.time != CurrentTime
|
|
||||||
&& ev_P.time - d->timestamp > 1U << 31 ) {
|
|
||||||
return; // too old or too new request
|
|
||||||
}
|
|
||||||
// kDebug() << "Got selection request";
|
|
||||||
bool handled = false;
|
|
||||||
if ( ev_P.target == Private::xa_multiple ) {
|
|
||||||
if ( ev_P.property != None ) {
|
|
||||||
const int MAX_ATOMS = 100; // no need to handle more?
|
|
||||||
int format;
|
|
||||||
Atom type;
|
|
||||||
unsigned long items;
|
|
||||||
unsigned long after;
|
|
||||||
unsigned char* data;
|
|
||||||
if ( XGetWindowProperty( QX11Info::display(), ev_P.requestor, ev_P.property, 0,
|
|
||||||
MAX_ATOMS, False, AnyPropertyType, &type, &format, &items, &after,
|
|
||||||
&data ) == Success && format == 32 && items % 2 == 0 ) {
|
|
||||||
bool handled_array[ MAX_ATOMS ];
|
|
||||||
Atom* atoms = reinterpret_cast< Atom* >( data );
|
|
||||||
for ( unsigned int i = 0; i < items / 2; ++i ) {
|
|
||||||
handled_array[ i ] = handle_selection( atoms[ i * 2 ], atoms[ i * 2 + 1 ], ev_P.requestor );
|
|
||||||
}
|
|
||||||
bool all_handled = true;
|
|
||||||
for ( unsigned int i = 0; i < items / 2; ++i ) {
|
|
||||||
if ( !handled_array[ i ] ) {
|
|
||||||
all_handled = false;
|
|
||||||
atoms[ i * 2 + 1 ] = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !all_handled ) {
|
|
||||||
XChangeProperty( QX11Info::display(), ev_P.requestor, ev_P.property, XA_ATOM,
|
|
||||||
32, PropModeReplace, reinterpret_cast< unsigned char* >( atoms ), items );
|
|
||||||
}
|
|
||||||
handled = true;
|
|
||||||
XFree( data );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ( ev_P.property == None ) { // obsolete client
|
|
||||||
ev_P.property = ev_P.target;
|
|
||||||
}
|
|
||||||
handled = handle_selection( ev_P.target, ev_P.property, ev_P.requestor );
|
|
||||||
}
|
|
||||||
XEvent ev;
|
|
||||||
ev.xselection.selection = ev_P.selection;
|
|
||||||
ev.xselection.type = SelectionNotify;
|
|
||||||
ev.xselection.display = QX11Info::display();
|
|
||||||
ev.xselection.requestor = ev_P.requestor;
|
|
||||||
ev.xselection.target = ev_P.target;
|
|
||||||
ev.xselection.property = handled ? ev_P.property : None;
|
|
||||||
XSendEvent( QX11Info::display(), ev_P.requestor, False, 0, &ev );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KSelectionOwner::handle_selection( Atom target_P, Atom property_P, Window requestor_P )
|
|
||||||
{
|
|
||||||
if ( target_P == Private::xa_timestamp ) {
|
|
||||||
// kDebug() << "Handling timestamp request";
|
|
||||||
XChangeProperty( QX11Info::display(), requestor_P, property_P, XA_INTEGER, 32,
|
|
||||||
PropModeReplace, reinterpret_cast< unsigned char* >( &d->timestamp ), 1 );
|
|
||||||
} else if ( target_P == Private::xa_targets ) {
|
|
||||||
replyTargets( property_P, requestor_P );
|
|
||||||
} else if ( genericReply( target_P, property_P, requestor_P )) {
|
|
||||||
; // handled
|
|
||||||
} else {
|
|
||||||
return false; // unknown
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KSelectionOwner::replyTargets( Atom property_P, Window requestor_P )
|
|
||||||
{
|
|
||||||
Atom atoms[ 3 ] = { Private::xa_multiple, Private::xa_timestamp, Private::xa_targets };
|
|
||||||
// kDebug() << "Handling targets request";
|
|
||||||
XChangeProperty( QX11Info::display(), requestor_P, property_P, XA_ATOM, 32, PropModeReplace,
|
|
||||||
reinterpret_cast< unsigned char* >( atoms ), 3 );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool KSelectionOwner::genericReply( Atom, Atom, Window )
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KSelectionOwner::getAtoms()
|
|
||||||
{
|
|
||||||
if ( Private::manager_atom == None ) {
|
|
||||||
Atom atoms[ 4 ];
|
|
||||||
const char* const names[] = { "MANAGER", "MULTIPLE", "TARGETS", "TIMESTAMP" };
|
|
||||||
XInternAtoms( QX11Info::display(), const_cast< char** >( names ), 4, False, atoms );
|
|
||||||
Private::manager_atom = atoms[ 0 ];
|
|
||||||
Private::xa_multiple = atoms[ 1 ];
|
|
||||||
Private::xa_targets = atoms[ 2 ];
|
|
||||||
Private::xa_timestamp = atoms[ 3 ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Atom KSelectionOwner::Private::manager_atom = None;
|
|
||||||
Atom KSelectionOwner::Private::xa_multiple = None;
|
|
||||||
Atom KSelectionOwner::Private::xa_targets = None;
|
|
||||||
Atom KSelectionOwner::Private::xa_timestamp = None;
|
|
||||||
|
|
||||||
//*******************************************
|
|
||||||
// KSelectionWatcher
|
|
||||||
//*******************************************
|
|
||||||
|
|
||||||
|
|
||||||
class KSelectionWatcher::Private : public QWidget
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Private( KSelectionWatcher* watcher_P, Atom selection_P, int screen_P )
|
|
||||||
: selection( selection_P ),
|
|
||||||
screen( screen_P >= 0 ? screen_P : DefaultScreen( QX11Info::display())),
|
|
||||||
selection_owner( None ),
|
|
||||||
watcher( watcher_P )
|
|
||||||
{
|
|
||||||
kapp->installX11EventFilter( this );
|
|
||||||
}
|
|
||||||
|
|
||||||
const Atom selection;
|
|
||||||
const int screen;
|
|
||||||
Window selection_owner;
|
|
||||||
static Atom manager_atom;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool x11Event( XEvent* ev_P )
|
|
||||||
{
|
|
||||||
watcher->filterEvent( ev_P );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
KSelectionWatcher* watcher;
|
|
||||||
};
|
|
||||||
|
|
||||||
KSelectionWatcher::KSelectionWatcher( Atom selection_P, int screen_P, QObject* parent_P )
|
|
||||||
: QObject( parent_P ),
|
|
||||||
d( new Private( this, selection_P, screen_P ))
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
KSelectionWatcher::KSelectionWatcher( const char* selection_P, int screen_P, QObject* parent_P )
|
|
||||||
: QObject( parent_P ),
|
|
||||||
d( new Private( this, XInternAtom( QX11Info::display(), selection_P, False ), screen_P ))
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
KSelectionWatcher::~KSelectionWatcher()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
void KSelectionWatcher::init()
|
|
||||||
{
|
|
||||||
if ( Private::manager_atom == None ) {
|
|
||||||
Display* const dpy = QX11Info::display();
|
|
||||||
Private::manager_atom = XInternAtom( dpy, "MANAGER", False );
|
|
||||||
XWindowAttributes attrs;
|
|
||||||
XGetWindowAttributes( dpy, RootWindow( dpy, d->screen ), &attrs );
|
|
||||||
long event_mask = attrs.your_event_mask;
|
|
||||||
// StructureNotifyMask on the root window is needed
|
|
||||||
XSelectInput( dpy, RootWindow( dpy, d->screen ), event_mask | StructureNotifyMask );
|
|
||||||
}
|
|
||||||
owner(); // trigger reading of current selection status
|
|
||||||
}
|
|
||||||
|
|
||||||
Window KSelectionWatcher::owner()
|
|
||||||
{
|
|
||||||
Display* const dpy = QX11Info::display();
|
|
||||||
KXErrorHandler handler;
|
|
||||||
Window current_owner = XGetSelectionOwner( dpy, d->selection );
|
|
||||||
if ( current_owner == None )
|
|
||||||
return None;
|
|
||||||
if ( current_owner == d->selection_owner ) {
|
|
||||||
return d->selection_owner;
|
|
||||||
}
|
|
||||||
XSelectInput( dpy, current_owner, StructureNotifyMask );
|
|
||||||
if ( !handler.error( true ) && current_owner == XGetSelectionOwner( dpy, d->selection )) {
|
|
||||||
// kDebug() << "isOwner: " << current_owner;
|
|
||||||
d->selection_owner = current_owner;
|
|
||||||
emit newOwner( d->selection_owner );
|
|
||||||
} else {
|
|
||||||
d->selection_owner = None;
|
|
||||||
}
|
|
||||||
return d->selection_owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
// void return value in order to allow more watchers in one process
|
|
||||||
void KSelectionWatcher::filterEvent( XEvent* ev_P )
|
|
||||||
{
|
|
||||||
if ( ev_P->type == ClientMessage ) {
|
|
||||||
// kDebug() << "got ClientMessage";
|
|
||||||
if ( ev_P->xclient.message_type != Private::manager_atom
|
|
||||||
|| ev_P->xclient.data.l[ 1 ] != static_cast< long >( d->selection )) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// kDebug() << "handling message";
|
|
||||||
if ( static_cast< long >( owner()) == ev_P->xclient.data.l[ 2 ] ) {
|
|
||||||
// owner() emits newOwner() if needed, no need to do it twice
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( ev_P->type == DestroyNotify ) {
|
|
||||||
if ( d->selection_owner == None || ev_P->xdestroywindow.window != d->selection_owner ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
d->selection_owner = None; // in case the exactly same ID gets reused as the owner
|
|
||||||
if ( owner() == None ) {
|
|
||||||
emit lostOwner(); // it must be safe to delete 'this' in a slot
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Atom KSelectionWatcher::Private::manager_atom = None;
|
|
||||||
|
|
||||||
#include "moc_kmanagerselection.cpp"
|
|
|
@ -1,212 +0,0 @@
|
||||||
/****************************************************************************
|
|
||||||
|
|
||||||
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
copy of this software and associated documentation files (the "Software"),
|
|
||||||
to deal in the Software without restriction, including without limitation
|
|
||||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
#ifndef KMANAGERSELECTION_H
|
|
||||||
#define KMANAGERSELECTION_H
|
|
||||||
|
|
||||||
#include <kdeui_export.h>
|
|
||||||
#include <QtCore/QObject>
|
|
||||||
|
|
||||||
#ifdef Q_WS_X11 // FIXME(E)
|
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <fixx11h.h>
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
This class implements claiming and owning manager selections, as described
|
|
||||||
in the ICCCM, section 2.8. The selection atom is passed to the constructor,
|
|
||||||
claim() attemps to claim ownership of the selection, release() gives up
|
|
||||||
the selection ownership. Signal lostOwnership() is emitted when the selection
|
|
||||||
is claimed by another owner.
|
|
||||||
@short ICCCM manager selection owner
|
|
||||||
*/
|
|
||||||
class KDEUI_EXPORT KSelectionOwner
|
|
||||||
: public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* This constructor initializes the object, but doesn't perform any
|
|
||||||
* operation on the selection.
|
|
||||||
*
|
|
||||||
* @param selection atom representing the manager selection
|
|
||||||
* @param screen X screen, or -1 for default
|
|
||||||
* @param parent parent object, or NULL if there is none
|
|
||||||
*/
|
|
||||||
explicit KSelectionOwner( Atom selection, int screen = -1, QObject* parent = NULL );
|
|
||||||
/**
|
|
||||||
* @overload
|
|
||||||
* This constructor accepts the selection name and creates the appropriate atom
|
|
||||||
* for it automatically.
|
|
||||||
*
|
|
||||||
* @param selection name of the manager selection
|
|
||||||
* @param screen X screen, or -1 for default
|
|
||||||
* @param parent parent object, or NULL if there is none
|
|
||||||
*/
|
|
||||||
explicit KSelectionOwner( const char* selection, int screen = -1, QObject* parent = NULL );
|
|
||||||
/**
|
|
||||||
* Destructor. Calls release().
|
|
||||||
*/
|
|
||||||
virtual ~KSelectionOwner();
|
|
||||||
/**
|
|
||||||
* This function attemps to claim ownership of the manager selection, using
|
|
||||||
* the current X timestamp. If @p force is false, and the selection is already
|
|
||||||
* owned, the selection is not claimed, and false is returned. If claiming
|
|
||||||
* is forced and the selection is owned by another client, it is waited for up to 1 second
|
|
||||||
* for the previous owner to disown the selection, if @p force_kill is true,
|
|
||||||
* and the previous owner fails to disown the selection in time,
|
|
||||||
* it will be forcibly killed. True is returned after successfully claiming
|
|
||||||
* ownership of the selection.
|
|
||||||
*/
|
|
||||||
bool claim( bool force, bool force_kill = true );
|
|
||||||
/**
|
|
||||||
* If the selection is owned, the ownership is given up.
|
|
||||||
*/
|
|
||||||
void release();
|
|
||||||
/**
|
|
||||||
* If the selection is owned, returns the window used internally
|
|
||||||
* for owning the selection.
|
|
||||||
*/
|
|
||||||
Window ownerWindow() const; // None if not owning the selection
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
bool filterEvent( XEvent* ev_P ); // internal
|
|
||||||
Q_SIGNALS:
|
|
||||||
/**
|
|
||||||
* This signal is emitted if the selection was owned and the ownership
|
|
||||||
* has been lost due to another client claiming it, this signal is emitted.
|
|
||||||
* IMPORTANT: It's not safe to delete the instance in a slot connected
|
|
||||||
* to this signal.
|
|
||||||
*/
|
|
||||||
void lostOwnership();
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* Called for every X event received on the window used for owning
|
|
||||||
* the selection. If true is returned, the event is filtered out.
|
|
||||||
*/
|
|
||||||
virtual bool handleMessage( XEvent* ev );
|
|
||||||
/**
|
|
||||||
* Called when a SelectionRequest event is received. A reply should
|
|
||||||
* be sent using the selection handling mechanism described in the ICCCM
|
|
||||||
* section 2.
|
|
||||||
*
|
|
||||||
* @param target requested target type
|
|
||||||
* @param property property to use for the reply data
|
|
||||||
* @param requestor requestor window
|
|
||||||
*/
|
|
||||||
virtual bool genericReply( Atom target, Atom property, Window requestor );
|
|
||||||
/**
|
|
||||||
* Called to announce the supported targets, as described in the ICCCM
|
|
||||||
* section 2.6. The default implementation announces the required targets
|
|
||||||
* MULTIPLE, TIMESTAMP and TARGETS.
|
|
||||||
*/
|
|
||||||
virtual void replyTargets( Atom property, Window requestor );
|
|
||||||
/**
|
|
||||||
* Called to create atoms needed for claiming the selection and
|
|
||||||
* communication using the selection handling mechanism. The default
|
|
||||||
* implementation must be called if reimplemented. This method
|
|
||||||
* may be called repeatedly.
|
|
||||||
*/
|
|
||||||
virtual void getAtoms();
|
|
||||||
/**
|
|
||||||
* Sets extra data to be sent in the message sent to root window
|
|
||||||
* after successfully claiming a selection. These extra data
|
|
||||||
* are in data.l[3] and data.l[4] fields of the XClientMessage.
|
|
||||||
*/
|
|
||||||
void setData( long extra1, long extra2 );
|
|
||||||
private:
|
|
||||||
void filter_selection_request( XSelectionRequestEvent& ev_P );
|
|
||||||
bool handle_selection( Atom target_P, Atom property_P, Window requestor_P );
|
|
||||||
|
|
||||||
class Private;
|
|
||||||
Private* const d;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
This class implements watching manager selections, as described in the ICCCM
|
|
||||||
section 2.8. It emits signal newOwner() when a new owner claim the selection,
|
|
||||||
and emits lostOwner() when the selection ownership is given up. To find
|
|
||||||
out current owner of the selection, owner() can be used.
|
|
||||||
@short ICCCM manager selection watching
|
|
||||||
*/
|
|
||||||
class KDEUI_EXPORT KSelectionWatcher
|
|
||||||
: public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* This constructor initializes the object, but doesn't perform any
|
|
||||||
* operation on the selection.
|
|
||||||
*
|
|
||||||
* @param selection atom representing the manager selection
|
|
||||||
* @param screen X screen, or -1 for default
|
|
||||||
* @param parent parent object, or NULL if there is none
|
|
||||||
*/
|
|
||||||
explicit KSelectionWatcher( Atom selection, int screen = -1, QObject* parent = NULL );
|
|
||||||
/**
|
|
||||||
* @overload
|
|
||||||
* This constructor accepts the selection name and creates the appropriate atom
|
|
||||||
* for it automatically.
|
|
||||||
*
|
|
||||||
* @param selection name of the manager selection
|
|
||||||
* @param screen X screen, or -1 for default
|
|
||||||
* @param parent parent object, or NULL if there is none
|
|
||||||
*/
|
|
||||||
explicit KSelectionWatcher( const char* selection, int screen = -1, QObject* parent = NULL );
|
|
||||||
virtual ~KSelectionWatcher();
|
|
||||||
/**
|
|
||||||
* Return the current owner of the manager selection, if any. Note that if the event
|
|
||||||
* informing about the owner change is still in the input queue, newOwner() might
|
|
||||||
* have been emitted yet.
|
|
||||||
*/
|
|
||||||
Window owner();
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
void filterEvent( XEvent* ev_P ); // internal
|
|
||||||
Q_SIGNALS:
|
|
||||||
/**
|
|
||||||
* This signal is emitted when the selection is successfully claimed by a new
|
|
||||||
* owner.
|
|
||||||
* @param owner the new owner of the selection
|
|
||||||
*/
|
|
||||||
void newOwner( Window owner );
|
|
||||||
/**
|
|
||||||
* This signal is emitted when the selection is given up, i.e. there's no
|
|
||||||
* owner. Note that the selection may be immediatelly claimed again,
|
|
||||||
* so the newOwner() signal may be emitted right after this one.
|
|
||||||
* It's safe to delete the instance in a slot connected to this signal.
|
|
||||||
*/
|
|
||||||
void lostOwner();
|
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
class Private;
|
|
||||||
Private* const d;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
144
kdeui/util/kselectionowner.cpp
Normal file
144
kdeui/util/kselectionowner.cpp
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
/* This file is part of the KDE libraries
|
||||||
|
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License version 2, as published by the Free Software Foundation.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public License
|
||||||
|
along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "kselectionowner.h"
|
||||||
|
#include "kxerrorhandler.h"
|
||||||
|
#include "kdebug.h"
|
||||||
|
|
||||||
|
#include <QX11Info>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#define KSELECTIONOWNER_TIMEOUT 400
|
||||||
|
#define KSELECTIONOWNER_SLEEPTIME 400
|
||||||
|
#define KSELECTIONOWNER_CHECKTIME 200
|
||||||
|
|
||||||
|
class KSelectionOwnerPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
KSelectionOwnerPrivate();
|
||||||
|
|
||||||
|
Atom x11atom;
|
||||||
|
Display* x11display;
|
||||||
|
int x11screen;
|
||||||
|
Window x11window;
|
||||||
|
};
|
||||||
|
|
||||||
|
KSelectionOwnerPrivate::KSelectionOwnerPrivate()
|
||||||
|
: x11atom(None),
|
||||||
|
x11display(QX11Info::display()),
|
||||||
|
x11screen(QX11Info::appScreen()),
|
||||||
|
x11window(None)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
KSelectionOwner::KSelectionOwner(const char* atom, const int screen, QObject *parent)
|
||||||
|
: QObject(parent),
|
||||||
|
d(new KSelectionOwnerPrivate())
|
||||||
|
{
|
||||||
|
d->x11atom = XInternAtom(d->x11display, atom, False);
|
||||||
|
if (screen >= 0) {
|
||||||
|
d->x11screen = screen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KSelectionOwner::~KSelectionOwner()
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KSelectionOwner::claim(const bool force)
|
||||||
|
{
|
||||||
|
Window currentowner = XGetSelectionOwner(d->x11display, d->x11atom);
|
||||||
|
if (currentowner != None && !force) {
|
||||||
|
kDebug() << "Selection is owned";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (currentowner != None) {
|
||||||
|
kDebug() << "Selection is owned, destroying owner";
|
||||||
|
XDestroyWindow(d->x11display, currentowner);
|
||||||
|
XFlush(d->x11display);
|
||||||
|
ushort counter = 0;
|
||||||
|
kDebug() << "Waiting for owner";
|
||||||
|
while (currentowner != None && counter < 10) {
|
||||||
|
currentowner = XGetSelectionOwner(d->x11display, d->x11atom);
|
||||||
|
QCoreApplication::processEvents(QEventLoop::AllEvents, KSELECTIONOWNER_TIMEOUT);
|
||||||
|
QThread::msleep(KSELECTIONOWNER_SLEEPTIME);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentowner != None) {
|
||||||
|
kDebug() << "Selection is owned, killing owner";
|
||||||
|
KXErrorHandler kx11errorhandler;
|
||||||
|
XKillClient(d->x11display, currentowner);
|
||||||
|
XFlush(d->x11display);
|
||||||
|
if (kx11errorhandler.error(true)) {
|
||||||
|
kWarning() << KXErrorHandler::errorMessage(kx11errorhandler.errorEvent());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kDebug() << "Creating selection owner";
|
||||||
|
XSetWindowAttributes x11attrs;
|
||||||
|
x11attrs.override_redirect = True;
|
||||||
|
d->x11window = XCreateWindow(
|
||||||
|
d->x11display, RootWindow(d->x11display, d->x11screen),
|
||||||
|
0, 0, 1, 1,
|
||||||
|
0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &x11attrs
|
||||||
|
);
|
||||||
|
XSetSelectionOwner(d->x11display, d->x11atom, d->x11window, CurrentTime);
|
||||||
|
XFlush(d->x11display);
|
||||||
|
QTimer::singleShot(KSELECTIONOWNER_CHECKTIME, this, SLOT(_checkOwnership()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSelectionOwner::release()
|
||||||
|
{
|
||||||
|
if (d->x11window == None) {
|
||||||
|
kDebug() << "No owner";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kDebug() << "Destroying owner window";
|
||||||
|
XDestroyWindow(d->x11display, d->x11window);
|
||||||
|
XFlush(d->x11display);
|
||||||
|
d->x11window = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window KSelectionOwner::ownerWindow() const
|
||||||
|
{
|
||||||
|
kDebug() << "Current selection owner is" << d->x11window;
|
||||||
|
return d->x11window;
|
||||||
|
}
|
||||||
|
|
||||||
|
void KSelectionOwner::_checkOwnership()
|
||||||
|
{
|
||||||
|
if (d->x11window == None) {
|
||||||
|
kDebug() << "Not going to poll";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// kDebug() << "Checking selection owner";
|
||||||
|
Window currentowner = XGetSelectionOwner(d->x11display, d->x11atom);
|
||||||
|
if (currentowner != d->x11window) {
|
||||||
|
kDebug() << "Selection owner changed";
|
||||||
|
emit lostOwnership();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QTimer::singleShot(KSELECTIONOWNER_CHECKTIME, this, SLOT(_checkOwnership()));
|
||||||
|
}
|
56
kdeui/util/kselectionowner.h
Normal file
56
kdeui/util/kselectionowner.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/* This file is part of the KDE libraries
|
||||||
|
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Library General Public
|
||||||
|
License version 2, as published by the Free Software Foundation.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Library General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Library General Public License
|
||||||
|
along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KSELECTIONOWNER_H
|
||||||
|
#define KSELECTIONOWNER_H
|
||||||
|
|
||||||
|
#include <kdeui_export.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <fixx11h.h>
|
||||||
|
|
||||||
|
class KSelectionOwnerPrivate;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Class to claim selection of X11 atom.
|
||||||
|
*/
|
||||||
|
class KDEUI_EXPORT KSelectionOwner : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
KSelectionOwner(const char* atom, const int screen = -1, QObject *parent = nullptr);
|
||||||
|
~KSelectionOwner();
|
||||||
|
|
||||||
|
bool claim(const bool force);
|
||||||
|
void release();
|
||||||
|
|
||||||
|
Window ownerWindow() const;
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void lostOwnership();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void _checkOwnership();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(KSelectionOwner);
|
||||||
|
KSelectionOwnerPrivate * const d;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KSELECTIONOWNER_H
|
Loading…
Add table
Reference in a new issue