summaryrefslogtreecommitdiffstats
path: root/kdecore/kmanagerselection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdecore/kmanagerselection.cpp')
-rw-r--r--kdecore/kmanagerselection.cpp490
1 files changed, 490 insertions, 0 deletions
diff --git a/kdecore/kmanagerselection.cpp b/kdecore/kmanagerselection.cpp
new file mode 100644
index 000000000..f82df9ff1
--- /dev/null
+++ b/kdecore/kmanagerselection.cpp
@@ -0,0 +1,490 @@
+/****************************************************************************
+
+ $Id$
+
+ 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.
+
+****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <qobject.h>
+#ifdef Q_WS_X11 // FIXME(E)
+
+#include "kmanagerselection.h"
+
+#include <kdebug.h>
+#include <qwidget.h>
+#include <kapplication.h>
+#include <kxerrorhandler.h>
+#include <X11/Xatom.h>
+
+class KSelectionOwnerPrivate
+ : public QWidget
+ {
+ public:
+ KSelectionOwnerPrivate( KSelectionOwner* owner );
+ protected:
+ virtual bool x11Event( XEvent* ev );
+ private:
+ KSelectionOwner* owner;
+ };
+
+KSelectionOwnerPrivate::KSelectionOwnerPrivate( KSelectionOwner* owner_P )
+ : owner( owner_P )
+ {
+ kapp->installX11EventFilter( this );
+ }
+
+bool KSelectionOwnerPrivate::x11Event( XEvent* ev_P )
+ {
+ return owner->filterEvent( ev_P );
+ }
+
+KSelectionOwner::KSelectionOwner( Atom selection_P, int screen_P, QObject* parent_P )
+ : QObject( parent_P ),
+ selection( selection_P ),
+ screen( screen_P >= 0 ? screen_P : DefaultScreen( qt_xdisplay())),
+ window( None ),
+ timestamp( CurrentTime ),
+ extra1( 0 ), extra2( 0 ),
+ d( new KSelectionOwnerPrivate( this ))
+ {
+ }
+
+KSelectionOwner::KSelectionOwner( const char* selection_P, int screen_P, QObject* parent_P )
+ : QObject( parent_P ),
+ selection( XInternAtom( qt_xdisplay(), selection_P, False )),
+ screen( screen_P >= 0 ? screen_P : DefaultScreen( qt_xdisplay())),
+ window( None ),
+ timestamp( CurrentTime ),
+ extra1( 0 ), extra2( 0 ),
+ d( new KSelectionOwnerPrivate( this ))
+ {
+ }
+
+KSelectionOwner::~KSelectionOwner()
+ {
+ release();
+ delete d;
+ }
+
+bool KSelectionOwner::claim( bool force_P, bool force_kill_P )
+ {
+ if( manager_atom == None )
+ getAtoms();
+ if( timestamp != CurrentTime )
+ release();
+ Display* const dpy = qt_xdisplay();
+ Window prev_owner = XGetSelectionOwner( dpy, selection );
+ if( prev_owner != None )
+ {
+ if( !force_P )
+ {
+// kdDebug() << "Selection already owned, failing" << endl;
+ return false;
+ }
+ XSelectInput( dpy, prev_owner, StructureNotifyMask );
+ }
+ XSetWindowAttributes attrs;
+ attrs.override_redirect = True;
+ window = XCreateWindow( dpy, RootWindow( dpy, screen ), 0, 0, 1, 1,
+ 0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &attrs );
+// kdDebug() << "Using owner window " << window << endl;
+ Atom tmp = XA_ATOM;
+ XSelectInput( dpy, window, PropertyChangeMask );
+ XChangeProperty( dpy, window, XA_ATOM, XA_ATOM, 32, PropModeReplace,
+ reinterpret_cast< unsigned char* >( &tmp ), 1 );
+ XEvent ev;
+ XSync( dpy, False );
+ XCheckTypedWindowEvent( dpy, window, PropertyNotify, &ev ); // get a timestamp
+ timestamp = ev.xproperty.time;
+ XSelectInput( dpy, window, StructureNotifyMask ); // for DestroyNotify
+ XSetSelectionOwner( dpy, selection, window, timestamp );
+ Window new_owner = XGetSelectionOwner( dpy, selection );
+ if( new_owner != window )
+ {
+// kdDebug() << "Failed to claim selection : " << new_owner << endl;
+ XDestroyWindow( dpy, window );
+ timestamp = CurrentTime;
+ return false;
+ }
+ if( prev_owner != None )
+ {
+// kdDebug() << "Waiting for previous owner to disown" << endl;
+ 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 )
+ {
+// kdDebug() << "Killing previous owner" << endl;
+ XKillClient( dpy, prev_owner );
+ }
+ break;
+ }
+ }
+ }
+ ev.type = ClientMessage;
+ ev.xclient.window = RootWindow( dpy, screen );
+ ev.xclient.display = dpy;
+ ev.xclient.message_type = manager_atom;
+ ev.xclient.format = 32;
+ ev.xclient.data.l[ 0 ] = timestamp;
+ ev.xclient.data.l[ 1 ] = selection;
+ ev.xclient.data.l[ 2 ] = window;
+ ev.xclient.data.l[ 3 ] = extra1;
+ ev.xclient.data.l[ 4 ] = extra2;
+ XSendEvent( dpy, RootWindow( dpy, screen ), False, StructureNotifyMask, &ev );
+// kdDebug() << "Claimed selection" << endl;
+ return true;
+ }
+
+// destroy resource first
+void KSelectionOwner::release()
+ {
+ if( timestamp == CurrentTime )
+ return;
+ XDestroyWindow( qt_xdisplay(), window ); // also makes the selection not owned
+// kdDebug() << "Releasing selection" << endl;
+ timestamp = CurrentTime;
+ }
+
+Window KSelectionOwner::ownerWindow() const
+ {
+ if( timestamp == CurrentTime )
+ return None;
+ return window;
+ }
+
+void KSelectionOwner::setData( long extra1_P, long extra2_P )
+ {
+ extra1 = extra1_P;
+ extra2 = extra2_P;
+ }
+
+bool KSelectionOwner::filterEvent( XEvent* ev_P )
+ {
+ if( timestamp != CurrentTime && ev_P->xany.window == window )
+ {
+ if( handleMessage( ev_P ))
+ return true;
+ }
+ switch( ev_P->type )
+ {
+ case SelectionClear:
+ {
+ if( timestamp == CurrentTime || ev_P->xselectionclear.selection != selection )
+ return false;
+ timestamp = CurrentTime;
+// kdDebug() << "Lost selection" << endl;
+ emit lostOwnership();
+ XSelectInput( qt_xdisplay(), window, 0 );
+ XDestroyWindow( qt_xdisplay(), window );
+ return false;
+ }
+ case DestroyNotify:
+ {
+ if( timestamp == CurrentTime || ev_P->xdestroywindow.window != window )
+ return false;
+ timestamp = CurrentTime;
+// kdDebug() << "Lost selection (destroyed)" << endl;
+ emit lostOwnership();
+ return false;
+ }
+ case SelectionNotify:
+ {
+ if( timestamp == CurrentTime || ev_P->xselection.selection != 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( timestamp == CurrentTime || ev_P.selection != selection )
+ return;
+ if( ev_P.time != CurrentTime
+ && ev_P.time - timestamp > 1U << 31 )
+ return; // too old or too new request
+// kdDebug() << "Got selection request" << endl;
+ bool handled = false;
+ if( ev_P.target == 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( qt_xdisplay(), 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( qt_xdisplay(), 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.type = SelectionNotify;
+ ev.xselection.display = qt_xdisplay();
+ ev.xselection.requestor = ev_P.requestor;
+ ev.xselection.target = ev_P.target;
+ ev.xselection.property = handled ? ev_P.property : None;
+ XSendEvent( qt_xdisplay(), ev_P.requestor, False, 0, &ev );
+ }
+
+bool KSelectionOwner::handle_selection( Atom target_P, Atom property_P, Window requestor_P )
+ {
+ if( target_P == xa_timestamp )
+ {
+// kdDebug() << "Handling timestamp request" << endl;
+ XChangeProperty( qt_xdisplay(), requestor_P, property_P, XA_INTEGER, 32,
+ PropModeReplace, reinterpret_cast< unsigned char* >( &timestamp ), 1 );
+ }
+ else if( target_P == 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 ] = { xa_multiple, xa_timestamp, xa_targets };
+// kdDebug() << "Handling targets request" << endl;
+ XChangeProperty( qt_xdisplay(), 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( manager_atom == None )
+ {
+ Atom atoms[ 4 ];
+ const char* const names[] =
+ { "MANAGER", "MULTIPLE", "TARGETS", "TIMESTAMP" };
+ XInternAtoms( qt_xdisplay(), const_cast< char** >( names ), 4, False, atoms );
+ manager_atom = atoms[ 0 ];
+ xa_multiple = atoms[ 1];
+ xa_targets = atoms[ 2 ];
+ xa_timestamp = atoms[ 3 ];
+ }
+ }
+
+Atom KSelectionOwner::manager_atom = None;
+Atom KSelectionOwner::xa_multiple = None;
+Atom KSelectionOwner::xa_targets = None;
+Atom KSelectionOwner::xa_timestamp = None;
+
+//*******************************************
+// KSelectionWatcher
+//*******************************************
+
+
+class KSelectionWatcherPrivate
+ : public QWidget
+ {
+ public:
+ KSelectionWatcherPrivate( KSelectionWatcher* watcher );
+ protected:
+ virtual bool x11Event( XEvent* ev );
+ private:
+ KSelectionWatcher* watcher;
+ };
+
+KSelectionWatcherPrivate::KSelectionWatcherPrivate( KSelectionWatcher* watcher_P )
+ : watcher( watcher_P )
+ {
+ kapp->installX11EventFilter( this );
+ }
+
+bool KSelectionWatcherPrivate::x11Event( XEvent* ev_P )
+ {
+ watcher->filterEvent( ev_P );
+ return false;
+ }
+
+
+KSelectionWatcher::KSelectionWatcher( Atom selection_P, int screen_P, QObject* parent_P )
+ : QObject( parent_P ),
+ selection( selection_P ),
+ screen( screen_P >= 0 ? screen_P : DefaultScreen( qt_xdisplay())),
+ selection_owner( None ),
+ d( new KSelectionWatcherPrivate( this ))
+ {
+ init();
+ }
+
+KSelectionWatcher::KSelectionWatcher( const char* selection_P, int screen_P, QObject* parent_P )
+ : QObject( parent_P ),
+ selection( XInternAtom( qt_xdisplay(), selection_P, False )),
+ screen( screen_P >= 0 ? screen_P : DefaultScreen( qt_xdisplay())),
+ selection_owner( None ),
+ d( new KSelectionWatcherPrivate( this ))
+ {
+ init();
+ }
+
+KSelectionWatcher::~KSelectionWatcher()
+ {
+ delete d;
+ }
+
+void KSelectionWatcher::init()
+ {
+ if( manager_atom == None )
+ {
+ Display* const dpy = qt_xdisplay();
+ manager_atom = XInternAtom( dpy, "MANAGER", False );
+ XWindowAttributes attrs;
+ XGetWindowAttributes( dpy, RootWindow( dpy, screen ), &attrs );
+ long event_mask = attrs.your_event_mask;
+ // StructureNotifyMask on the root window is needed
+ XSelectInput( dpy, RootWindow( dpy, screen ), event_mask | StructureNotifyMask );
+ }
+ }
+
+Window KSelectionWatcher::owner()
+ {
+ Display* const dpy = qt_xdisplay();
+ KXErrorHandler handler;
+ Window current_owner = XGetSelectionOwner( dpy, selection );
+ if( current_owner == None )
+ return None;
+ if( current_owner == selection_owner )
+ return selection_owner;
+ XSelectInput( dpy, current_owner, StructureNotifyMask );
+ if( !handler.error( true ) && current_owner == XGetSelectionOwner( dpy, selection ))
+ {
+// kdDebug() << "isOwner: " << current_owner << endl;
+ selection_owner = current_owner;
+ emit newOwner( selection_owner );
+ }
+ else
+ selection_owner = None;
+ return 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 )
+ {
+// kdDebug() << "got ClientMessage" << endl;
+ if( ev_P->xclient.message_type != manager_atom
+ || ev_P->xclient.data.l[ 1 ] != static_cast< long >( selection ))
+ return;
+// kdDebug() << "handling message" << endl;
+ 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( selection_owner == None || ev_P->xdestroywindow.window != selection_owner )
+ return;
+ 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;
+ }
+ return;
+ }
+
+Atom KSelectionWatcher::manager_atom = None;
+
+void KSelectionOwner::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+void KSelectionWatcher::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+#include "kmanagerselection.moc"
+#endif