diff --git a/includes/CMakeLists.txt b/includes/CMakeLists.txt index d3288974..c5ced18c 100644 --- a/includes/CMakeLists.txt +++ b/includes/CMakeLists.txt @@ -251,7 +251,6 @@ install( KSaveFile KSelectAction KSelectionOwner - KSelectionWatcher KSelector KSeparator KService diff --git a/includes/KSelectionOwner b/includes/KSelectionOwner index c618d274..29b0a760 100644 --- a/includes/KSelectionOwner +++ b/includes/KSelectionOwner @@ -1 +1 @@ -#include "../kmanagerselection.h" +#include "../kselectionowner.h" diff --git a/includes/KSelectionWatcher b/includes/KSelectionWatcher deleted file mode 100644 index c618d274..00000000 --- a/includes/KSelectionWatcher +++ /dev/null @@ -1 +0,0 @@ -#include "../kmanagerselection.h" diff --git a/kdeui/CMakeLists.txt b/kdeui/CMakeLists.txt index c3e571d1..cd6c6f40 100644 --- a/kdeui/CMakeLists.txt +++ b/kdeui/CMakeLists.txt @@ -181,7 +181,7 @@ set(kdeui_LIB_SRCS util/kcursor.cpp util/kguiitem.cpp util/kkeyserver.cpp - util/kmanagerselection.cpp + util/kselectionowner.cpp util/knumvalidator.cpp util/kpassivepopup.cpp util/kpassivepopupmessagehandler.cpp @@ -500,7 +500,7 @@ install( util/kcursor.h util/kguiitem.h util/kkeyserver.h - util/kmanagerselection.h + util/kselectionowner.h util/knumvalidator.h util/kpassivepopup.h util/kpassivepopupmessagehandler.h diff --git a/kdeui/tests/CMakeLists.txt b/kdeui/tests/CMakeLists.txt index 2b5bebe7..0b04532c 100644 --- a/kdeui/tests/CMakeLists.txt +++ b/kdeui/tests/CMakeLists.txt @@ -143,9 +143,7 @@ if (Q_WS_X11) fixx11h_test fixx11h_test2 kxerrorhandlertest - kmanagerselectiontest ) - target_link_libraries(kdeui-kmanagerselectiontest ${X11_X11_LIB}) target_link_libraries(kdeui-kxerrorhandlertest ${X11_X11_LIB}) endif (Q_WS_X11) diff --git a/kdeui/tests/kmanagerselectiontest.cpp b/kdeui/tests/kmanagerselectiontest.cpp deleted file mode 100644 index 8e1c430f..00000000 --- a/kdeui/tests/kmanagerselectiontest.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/* - This file is part of the KDE Libraries - - Copyright (C) 2009 Lubos Lunak - - 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 - -#include -#include -#include - -#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 - -// 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(); /*as done by kapplication*/ \ - qRegisterMetaType(); \ - 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) diff --git a/kdeui/tests/kmanagerselectiontest.h b/kdeui/tests/kmanagerselectiontest.h deleted file mode 100644 index 4f444749..00000000 --- a/kdeui/tests/kmanagerselectiontest.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - This file is part of the KDE Libraries - - Copyright (C) 2009 Lubos Lunak - - 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 -#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 diff --git a/kdeui/util/kmanagerselection.cpp b/kdeui/util/kmanagerselection.cpp deleted file mode 100644 index f14093fc..00000000 --- a/kdeui/util/kmanagerselection.cpp +++ /dev/null @@ -1,468 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2003 Lubos Lunak - -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 - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - - -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" diff --git a/kdeui/util/kmanagerselection.h b/kdeui/util/kmanagerselection.h deleted file mode 100644 index 6b83950b..00000000 --- a/kdeui/util/kmanagerselection.h +++ /dev/null @@ -1,212 +0,0 @@ -/**************************************************************************** - - Copyright (C) 2003 Lubos Lunak - -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 -#include - -#ifdef Q_WS_X11 // FIXME(E) - -#include -#include - - -/** - 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 diff --git a/kdeui/util/kselectionowner.cpp b/kdeui/util/kselectionowner.cpp new file mode 100644 index 00000000..272f2244 --- /dev/null +++ b/kdeui/util/kselectionowner.cpp @@ -0,0 +1,144 @@ +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev + + 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 +#include +#include +#include + +#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())); +} diff --git a/kdeui/util/kselectionowner.h b/kdeui/util/kselectionowner.h new file mode 100644 index 00000000..2c2c21d2 --- /dev/null +++ b/kdeui/util/kselectionowner.h @@ -0,0 +1,56 @@ +/* This file is part of the KDE libraries + Copyright (C) 2022 Ivailo Monev + + 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 + +#include +#include +#include + +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