• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDEUI

kmanagerselection.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 
00003  Copyright (C) 2003 Lubos Lunak        <l.lunak@kde.org>
00004 
00005 Permission is hereby granted, free of charge, to any person obtaining a
00006 copy of this software and associated documentation files (the "Software"),
00007 to deal in the Software without restriction, including without limitation
00008 the rights to use, copy, modify, merge, publish, distribute, sublicense,
00009 and/or sell copies of the Software, and to permit persons to whom the
00010 Software is furnished to do so, subject to the following conditions:
00011 
00012 The above copyright notice and this permission notice shall be included in
00013 all copies or substantial portions of the Software.
00014 
00015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00018 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00019 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00020 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00021 DEALINGS IN THE SOFTWARE.
00022 
00023 ****************************************************************************/
00024 
00025 #include "kmanagerselection.h"
00026 
00027 #include <config.h>
00028 
00029 #ifdef HAVE_SYS_TYPES_H
00030 #include <sys/types.h>
00031 #endif
00032 
00033 #ifdef HAVE_SYS_TIME_H
00034 #include <sys/time.h>
00035 #endif
00036 
00037 #ifdef HAVE_UNISTD_H
00038 #include <unistd.h>
00039 #endif
00040 
00041 #include <QtCore/QObject>
00042 #ifdef Q_WS_X11 // FIXME(E)
00043 
00044 #include <qx11info_x11.h>
00045 #include <qwidget.h>
00046 #include <kdebug.h>
00047 #include <kapplication.h>
00048 #include <kxerrorhandler.h>
00049 #include <X11/Xatom.h>
00050 
00051 
00052 class KSelectionOwner::Private : public QWidget
00053 {
00054 public:
00055     Private( KSelectionOwner* owner_P, Atom selection_P, int screen_P )
00056         : selection( selection_P ),
00057           screen( screen_P >= 0 ? screen_P : DefaultScreen( QX11Info::display() ) ),
00058           window( None ),
00059           timestamp( CurrentTime ),
00060           extra1( 0 ),
00061           extra2( 0 ),
00062           owner( owner_P )
00063     {
00064         kapp->installX11EventFilter( this );
00065     }
00066 
00067     const Atom selection;
00068     const int screen;
00069     Window window;
00070     Time timestamp;
00071     long extra1, extra2;
00072     static Atom manager_atom;
00073     static Atom xa_multiple;
00074     static Atom xa_targets;
00075     static Atom xa_timestamp;
00076 
00077 protected:
00078     virtual bool x11Event( XEvent* ev_P )
00079     {
00080         return owner->filterEvent( ev_P );
00081     }
00082 
00083 private:
00084     KSelectionOwner* owner;
00085 };
00086 
00087     
00088 KSelectionOwner::KSelectionOwner( Atom selection_P, int screen_P, QObject* parent_P )
00089     :   QObject( parent_P ),
00090         d( new Private( this, selection_P, screen_P ) )
00091 {
00092 }
00093 
00094 KSelectionOwner::KSelectionOwner( const char* selection_P, int screen_P, QObject* parent_P )
00095     :   QObject( parent_P ),
00096         d( new Private( this, XInternAtom( QX11Info::display(), selection_P, False ), screen_P ) )
00097 {
00098 }
00099 
00100 KSelectionOwner::~KSelectionOwner()
00101 {
00102     release();
00103     delete d;
00104 }
00105 
00106 bool KSelectionOwner::claim( bool force_P, bool force_kill_P )
00107     {
00108     if( Private::manager_atom == None )
00109         getAtoms();
00110     if( d->timestamp != CurrentTime )
00111         release();
00112     Display* const dpy = QX11Info::display();
00113     Window prev_owner = XGetSelectionOwner( dpy, d->selection );
00114     if( prev_owner != None )
00115         {
00116         if( !force_P )
00117             {
00118 //            kDebug() << "Selection already owned, failing";
00119             return false;
00120             }
00121         XSelectInput( dpy, prev_owner, StructureNotifyMask );
00122         }
00123     XSetWindowAttributes attrs;
00124     attrs.override_redirect = True;
00125     d->window = XCreateWindow( dpy, RootWindow( dpy, d->screen ), 0, 0, 1, 1, 
00126         0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &attrs );
00127 //    kDebug() << "Using owner window " << window;
00128     Atom tmp = XA_ATOM;
00129     XSelectInput( dpy, d->window, PropertyChangeMask );
00130     XChangeProperty( dpy, d->window, XA_ATOM, XA_ATOM, 32, PropModeReplace,
00131         reinterpret_cast< unsigned char* >( &tmp ), 1 );
00132     XEvent ev;
00133     XSync( dpy, False );
00134     XCheckTypedWindowEvent( dpy, d->window, PropertyNotify, &ev ); // get a timestamp
00135     d->timestamp = ev.xproperty.time;
00136     XSelectInput( dpy, d->window, StructureNotifyMask ); // for DestroyNotify
00137     XSetSelectionOwner( dpy, d->selection, d->window, d->timestamp );
00138     Window new_owner = XGetSelectionOwner( dpy, d->selection );
00139     if( new_owner != d->window )
00140         {
00141 //        kDebug() << "Failed to claim selection : " << new_owner;
00142         XDestroyWindow( dpy, d->window );
00143         d->timestamp = CurrentTime;
00144         return false;
00145         }
00146     if( prev_owner != None )
00147         {
00148 //        kDebug() << "Waiting for previous owner to disown";
00149         for( int cnt = 0;
00150              ;
00151              ++cnt )
00152             {
00153             if( XCheckTypedWindowEvent( dpy, prev_owner, DestroyNotify, &ev ) == True )
00154                 break;
00155             struct timeval tm = { 0, 50000 }; // 50 ms
00156             select( 0, NULL, NULL, NULL, &tm );
00157             if( cnt == 19 )
00158                 {
00159                 if( force_kill_P )
00160                     {
00161                     KXErrorHandler err;
00162 //                    kDebug() << "Killing previous owner";
00163                     XKillClient( dpy, prev_owner );
00164                     err.error( true ); // ignore errors when killing
00165                     }
00166                 break;
00167                 }
00168             }
00169         }
00170     ev.type = ClientMessage;
00171     ev.xclient.window = RootWindow( dpy, d->screen );
00172     ev.xclient.display = dpy;
00173     ev.xclient.message_type = Private::manager_atom;
00174     ev.xclient.format = 32;
00175     ev.xclient.data.l[ 0 ] = d->timestamp;
00176     ev.xclient.data.l[ 1 ] = d->selection;
00177     ev.xclient.data.l[ 2 ] = d->window;
00178     ev.xclient.data.l[ 3 ] = d->extra1;
00179     ev.xclient.data.l[ 4 ] = d->extra2;
00180     XSendEvent( dpy, RootWindow( dpy, d->screen ), False, StructureNotifyMask, &ev );
00181 //    kDebug() << "Claimed selection";
00182     return true;
00183     }
00184 
00185 // destroy resource first
00186 void KSelectionOwner::release()
00187     {
00188     if( d->timestamp == CurrentTime )
00189         return;
00190     XDestroyWindow( QX11Info::display(), d->window ); // also makes the selection not owned
00191 //    kDebug() << "Releasing selection";
00192     d->timestamp = CurrentTime;
00193     }
00194 
00195 Window KSelectionOwner::ownerWindow() const
00196     {
00197     if( d->timestamp == CurrentTime )
00198         return None;
00199     return d->window;
00200     }
00201 
00202 void KSelectionOwner::setData( long extra1_P, long extra2_P )
00203     {
00204     d->extra1 = extra1_P;
00205     d->extra2 = extra2_P;
00206     }
00207     
00208 bool KSelectionOwner::filterEvent( XEvent* ev_P )
00209     {
00210     if( d->timestamp != CurrentTime && ev_P->xany.window == d->window )
00211         {
00212         if( handleMessage( ev_P ))
00213             return true;
00214         }
00215     switch( ev_P->type )
00216     {
00217     case SelectionClear:
00218         {
00219         if( d->timestamp == CurrentTime || ev_P->xselectionclear.selection != d->selection )
00220             return false;
00221         d->timestamp = CurrentTime;
00222 //      kDebug() << "Lost selection";
00223             Window window = d->window;
00224         emit lostOwnership();
00225         XSelectInput( QX11Info::display(), window, 0 );
00226         XDestroyWindow( QX11Info::display(), window );
00227       return true;
00228         }
00229     case DestroyNotify:
00230         {
00231         if( d->timestamp == CurrentTime || ev_P->xdestroywindow.window != d->window )
00232             return false;
00233         d->timestamp = CurrentTime;
00234 //      kDebug() << "Lost selection (destroyed)";
00235         emit lostOwnership();
00236       return true;
00237         }
00238     case SelectionNotify:
00239         {
00240         if( d->timestamp == CurrentTime || ev_P->xselection.selection != d->selection )
00241             return false;
00242         // ignore?
00243       return false;
00244         }
00245     case SelectionRequest:
00246         filter_selection_request( ev_P->xselectionrequest );
00247       return false;
00248     }
00249     return false;
00250     }
00251 
00252 bool KSelectionOwner::handleMessage( XEvent* )
00253     {
00254     return false;
00255     }
00256 
00257 void KSelectionOwner::filter_selection_request( XSelectionRequestEvent& ev_P )
00258     {
00259     if( d->timestamp == CurrentTime || ev_P.selection != d->selection )
00260         return;
00261     if( ev_P.time != CurrentTime
00262         && ev_P.time - d->timestamp > 1U << 31 )
00263         return; // too old or too new request
00264 //    kDebug() << "Got selection request";
00265     bool handled = false;
00266     if( ev_P.target == Private::xa_multiple )
00267         {
00268         if( ev_P.property != None )
00269             {
00270             const int MAX_ATOMS = 100; // no need to handle more?
00271             int format;
00272             Atom type;
00273             unsigned long items;
00274             unsigned long after;
00275             unsigned char* data;
00276             if( XGetWindowProperty( QX11Info::display(), ev_P.requestor, ev_P.property, 0,
00277                 MAX_ATOMS, False, AnyPropertyType, &type, &format, &items, &after,
00278                 &data ) == Success && format == 32 && items % 2 == 0 )
00279                 {
00280                 bool handled_array[ MAX_ATOMS ];
00281                 Atom* atoms = reinterpret_cast< Atom* >( data );
00282                 for( unsigned int i = 0;
00283                      i < items / 2;
00284                      ++i )
00285                     handled_array[ i ] = handle_selection(
00286                         atoms[ i * 2 ], atoms[ i * 2 + 1 ], ev_P.requestor );
00287                 bool all_handled = true;
00288                 for( unsigned int i = 0;
00289                      i < items / 2;
00290                      ++i )
00291                     if( !handled_array[ i ] )
00292                         {
00293                         all_handled = false;
00294                         atoms[ i * 2 + 1 ] = None;
00295                         }
00296                 if( !all_handled )
00297                     XChangeProperty( QX11Info::display(), ev_P.requestor, ev_P.property, XA_ATOM,
00298                         32, PropModeReplace, reinterpret_cast< unsigned char* >( atoms ), items );
00299                 handled = true;
00300                 XFree( data );
00301                 }
00302             }
00303         }
00304     else
00305         {
00306         if( ev_P.property == None ) // obsolete client
00307             ev_P.property = ev_P.target;
00308         handled = handle_selection( ev_P.target, ev_P.property, ev_P.requestor );
00309         }
00310     XEvent ev;
00311     ev.xselection.selection = ev_P.selection;
00312     ev.xselection.type = SelectionNotify;
00313     ev.xselection.display = QX11Info::display();
00314     ev.xselection.requestor = ev_P.requestor;
00315     ev.xselection.target = ev_P.target;
00316     ev.xselection.property = handled ? ev_P.property : None;
00317     XSendEvent( QX11Info::display(), ev_P.requestor, False, 0, &ev );
00318     }
00319 
00320 bool KSelectionOwner::handle_selection( Atom target_P, Atom property_P, Window requestor_P )
00321     {
00322     if( target_P == Private::xa_timestamp )
00323         {
00324 //        kDebug() << "Handling timestamp request";
00325         XChangeProperty( QX11Info::display(), requestor_P, property_P, XA_INTEGER, 32,
00326             PropModeReplace, reinterpret_cast< unsigned char* >( &d->timestamp ), 1 );
00327         }
00328     else if( target_P == Private::xa_targets )
00329         replyTargets( property_P, requestor_P );
00330     else if( genericReply( target_P, property_P, requestor_P ))
00331         ; // handled
00332     else
00333         return false; // unknown
00334     return true;
00335     }
00336 
00337 void KSelectionOwner::replyTargets( Atom property_P, Window requestor_P )
00338     {
00339     Atom atoms[ 3 ] = { Private::xa_multiple, Private::xa_timestamp, Private::xa_targets };
00340 //    kDebug() << "Handling targets request";
00341     XChangeProperty( QX11Info::display(), requestor_P, property_P, XA_ATOM, 32, PropModeReplace,
00342         reinterpret_cast< unsigned char* >( atoms ), 3 );
00343     }
00344 
00345 bool KSelectionOwner::genericReply( Atom, Atom, Window )
00346     {
00347     return false;
00348     }
00349 
00350 void KSelectionOwner::getAtoms()
00351     {
00352     if( Private::manager_atom == None )
00353         {
00354         Atom atoms[ 4 ];
00355         const char* const names[] =
00356             { "MANAGER", "MULTIPLE", "TARGETS", "TIMESTAMP" };
00357         XInternAtoms( QX11Info::display(), const_cast< char** >( names ), 4, False, atoms );
00358         Private::manager_atom = atoms[ 0 ];
00359         Private::xa_multiple = atoms[ 1];
00360         Private::xa_targets = atoms[ 2 ];
00361         Private::xa_timestamp = atoms[ 3 ];
00362         }
00363     }
00364 
00365 Atom KSelectionOwner::Private::manager_atom = None;
00366 Atom KSelectionOwner::Private::xa_multiple = None;
00367 Atom KSelectionOwner::Private::xa_targets = None;
00368 Atom KSelectionOwner::Private::xa_timestamp = None;
00369 
00370 //*******************************************
00371 // KSelectionWatcher
00372 //*******************************************
00373 
00374 
00375 class KSelectionWatcher::Private : public QWidget
00376 {
00377 public:
00378     Private( KSelectionWatcher* watcher_P, Atom selection_P, int screen_P )
00379         : selection( selection_P ),
00380           screen( screen_P >= 0 ? screen_P : DefaultScreen( QX11Info::display())),
00381           selection_owner( None ),
00382           watcher( watcher_P )
00383     {
00384         kapp->installX11EventFilter( this );
00385     }
00386 
00387     const Atom selection;
00388     const int screen;
00389     Window selection_owner;
00390     static Atom manager_atom;
00391 
00392 protected:
00393     virtual bool x11Event( XEvent* ev_P )
00394     {
00395         watcher->filterEvent( ev_P );
00396         return false;
00397     }
00398 
00399 private:
00400     KSelectionWatcher* watcher;
00401 };
00402 
00403 KSelectionWatcher::KSelectionWatcher( Atom selection_P, int screen_P, QObject* parent_P )
00404     :   QObject( parent_P ),
00405         d( new Private( this, selection_P, screen_P ))
00406     {
00407     init();
00408     }
00409     
00410 KSelectionWatcher::KSelectionWatcher( const char* selection_P, int screen_P, QObject* parent_P )
00411     :   QObject( parent_P ),
00412         d( new Private( this, XInternAtom( QX11Info::display(), selection_P, False ), screen_P ))
00413     {
00414     init();
00415     }
00416 
00417 KSelectionWatcher::~KSelectionWatcher()
00418     {
00419     delete d;
00420     }
00421     
00422 void KSelectionWatcher::init()
00423     {
00424     if( Private::manager_atom == None )
00425         {
00426         Display* const dpy = QX11Info::display();
00427         Private::manager_atom = XInternAtom( dpy, "MANAGER", False );
00428         XWindowAttributes attrs;
00429         XGetWindowAttributes( dpy, RootWindow( dpy, d->screen ), &attrs );
00430         long event_mask = attrs.your_event_mask;
00431         // StructureNotifyMask on the root window is needed
00432         XSelectInput( dpy, RootWindow( dpy, d->screen ), event_mask | StructureNotifyMask );
00433         }
00434     owner(); // trigger reading of current selection status
00435     }    
00436 
00437 Window KSelectionWatcher::owner()
00438     {
00439     Display* const dpy = QX11Info::display();
00440     KXErrorHandler handler;
00441     Window current_owner = XGetSelectionOwner( dpy, d->selection );
00442     if( current_owner == None )
00443         return None;
00444     if( current_owner == d->selection_owner )
00445         return d->selection_owner;
00446     XSelectInput( dpy, current_owner, StructureNotifyMask );
00447     if( !handler.error( true ) && current_owner == XGetSelectionOwner( dpy, d->selection ))
00448         {
00449 //        kDebug() << "isOwner: " << current_owner;
00450         d->selection_owner = current_owner;
00451         emit newOwner( d->selection_owner );
00452         }
00453     else
00454         d->selection_owner = None;
00455     return d->selection_owner;
00456     }
00457 
00458 // void return value in order to allow more watchers in one process
00459 void KSelectionWatcher::filterEvent( XEvent* ev_P )
00460     {
00461     if( ev_P->type == ClientMessage )
00462         {
00463 //        kDebug() << "got ClientMessage";
00464         if( ev_P->xclient.message_type != Private::manager_atom
00465             || ev_P->xclient.data.l[ 1 ] != static_cast< long >( d->selection ))
00466             return;
00467 //        kDebug() << "handling message";
00468         if( static_cast< long >( owner()) == ev_P->xclient.data.l[ 2 ] )
00469             {
00470             // owner() emits newOwner() if needed, no need to do it twice
00471             }
00472         return;
00473         }
00474     if( ev_P->type == DestroyNotify )
00475         {
00476         if( d->selection_owner == None || ev_P->xdestroywindow.window != d->selection_owner )
00477             return;
00478         d->selection_owner = None; // in case the exactly same ID gets reused as the owner
00479         if( owner() == None )
00480             emit lostOwner(); // it must be safe to delete 'this' in a slot
00481         return;
00482         }
00483     return;
00484     }
00485 
00486 Atom KSelectionWatcher::Private::manager_atom = None;
00487 
00488 #include "kmanagerselection.moc"
00489 #endif

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal