• Skip to content
  • Skip to link menu
KDE 4.1 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.type = SelectionNotify;
00312     ev.xselection.display = QX11Info::display();
00313     ev.xselection.requestor = ev_P.requestor;
00314     ev.xselection.target = ev_P.target;
00315     ev.xselection.property = handled ? ev_P.property : None;
00316     XSendEvent( QX11Info::display(), ev_P.requestor, False, 0, &ev );
00317     }
00318 
00319 bool KSelectionOwner::handle_selection( Atom target_P, Atom property_P, Window requestor_P )
00320     {
00321     if( target_P == Private::xa_timestamp )
00322         {
00323 //        kDebug() << "Handling timestamp request";
00324         XChangeProperty( QX11Info::display(), requestor_P, property_P, XA_INTEGER, 32,
00325             PropModeReplace, reinterpret_cast< unsigned char* >( &d->timestamp ), 1 );
00326         }
00327     else if( target_P == Private::xa_targets )
00328         replyTargets( property_P, requestor_P );
00329     else if( genericReply( target_P, property_P, requestor_P ))
00330         ; // handled
00331     else
00332         return false; // unknown
00333     return true;
00334     }
00335 
00336 void KSelectionOwner::replyTargets( Atom property_P, Window requestor_P )
00337     {
00338     Atom atoms[ 3 ] = { Private::xa_multiple, Private::xa_timestamp, Private::xa_targets };
00339 //    kDebug() << "Handling targets request";
00340     XChangeProperty( QX11Info::display(), requestor_P, property_P, XA_ATOM, 32, PropModeReplace,
00341         reinterpret_cast< unsigned char* >( atoms ), 3 );
00342     }
00343 
00344 bool KSelectionOwner::genericReply( Atom, Atom, Window )
00345     {
00346     return false;
00347     }
00348 
00349 void KSelectionOwner::getAtoms()
00350     {
00351     if( Private::manager_atom == None )
00352         {
00353         Atom atoms[ 4 ];
00354         const char* const names[] =
00355             { "MANAGER", "MULTIPLE", "TARGETS", "TIMESTAMP" };
00356         XInternAtoms( QX11Info::display(), const_cast< char** >( names ), 4, False, atoms );
00357         Private::manager_atom = atoms[ 0 ];
00358         Private::xa_multiple = atoms[ 1];
00359         Private::xa_targets = atoms[ 2 ];
00360         Private::xa_timestamp = atoms[ 3 ];
00361         }
00362     }
00363 
00364 Atom KSelectionOwner::Private::manager_atom = None;
00365 Atom KSelectionOwner::Private::xa_multiple = None;
00366 Atom KSelectionOwner::Private::xa_targets = None;
00367 Atom KSelectionOwner::Private::xa_timestamp = None;
00368 
00369 //*******************************************
00370 // KSelectionWatcher
00371 //*******************************************
00372 
00373 
00374 class KSelectionWatcher::Private : public QWidget
00375 {
00376 public:
00377     Private( KSelectionWatcher* watcher_P, Atom selection_P, int screen_P )
00378         : selection( selection_P ),
00379           screen( screen_P >= 0 ? screen_P : DefaultScreen( QX11Info::display())),
00380           selection_owner( None ),
00381           watcher( watcher_P )
00382     {
00383         kapp->installX11EventFilter( this );
00384     }
00385 
00386     const Atom selection;
00387     const int screen;
00388     Window selection_owner;
00389     static Atom manager_atom;
00390 
00391 protected:
00392     virtual bool x11Event( XEvent* ev_P )
00393     {
00394         watcher->filterEvent( ev_P );
00395         return false;
00396     }
00397 
00398 private:
00399     KSelectionWatcher* watcher;
00400 };
00401 
00402 KSelectionWatcher::KSelectionWatcher( Atom selection_P, int screen_P, QObject* parent_P )
00403     :   QObject( parent_P ),
00404         d( new Private( this, selection_P, screen_P ))
00405     {
00406     init();
00407     }
00408     
00409 KSelectionWatcher::KSelectionWatcher( const char* selection_P, int screen_P, QObject* parent_P )
00410     :   QObject( parent_P ),
00411         d( new Private( this, XInternAtom( QX11Info::display(), selection_P, False ), screen_P ))
00412     {
00413     init();
00414     }
00415 
00416 KSelectionWatcher::~KSelectionWatcher()
00417     {
00418     delete d;
00419     }
00420     
00421 void KSelectionWatcher::init()
00422     {
00423     if( Private::manager_atom == None )
00424         {
00425         Display* const dpy = QX11Info::display();
00426         Private::manager_atom = XInternAtom( dpy, "MANAGER", False );
00427         XWindowAttributes attrs;
00428         XGetWindowAttributes( dpy, RootWindow( dpy, d->screen ), &attrs );
00429         long event_mask = attrs.your_event_mask;
00430         // StructureNotifyMask on the root window is needed
00431         XSelectInput( dpy, RootWindow( dpy, d->screen ), event_mask | StructureNotifyMask );
00432         }
00433     }    
00434 
00435 Window KSelectionWatcher::owner()
00436     {
00437     Display* const dpy = QX11Info::display();
00438     KXErrorHandler handler;
00439     Window current_owner = XGetSelectionOwner( dpy, d->selection );
00440     if( current_owner == None )
00441         return None;
00442     if( current_owner == d->selection_owner )
00443         return d->selection_owner;
00444     XSelectInput( dpy, current_owner, StructureNotifyMask );
00445     if( !handler.error( true ) && current_owner == XGetSelectionOwner( dpy, d->selection ))
00446         {
00447 //        kDebug() << "isOwner: " << current_owner;
00448         d->selection_owner = current_owner;
00449         emit newOwner( d->selection_owner );
00450         }
00451     else
00452         d->selection_owner = None;
00453     return d->selection_owner;
00454     }
00455 
00456 // void return value in order to allow more watchers in one process
00457 void KSelectionWatcher::filterEvent( XEvent* ev_P )
00458     {
00459     if( ev_P->type == ClientMessage )
00460         {
00461 //        kDebug() << "got ClientMessage";
00462         if( ev_P->xclient.message_type != Private::manager_atom
00463             || ev_P->xclient.data.l[ 1 ] != static_cast< long >( d->selection ))
00464             return;
00465 //        kDebug() << "handling message";
00466         if( static_cast< long >( owner()) == ev_P->xclient.data.l[ 2 ] )
00467             {
00468             // owner() emits newOwner() if needed, no need to do it twice
00469             }
00470         return;
00471         }
00472     if( ev_P->type == DestroyNotify )
00473         {
00474         if( d->selection_owner == None || ev_P->xdestroywindow.window != d->selection_owner )
00475             return;
00476         d->selection_owner = None; // in case the exactly same ID gets reused as the owner
00477         if( owner() == None )
00478             emit lostOwner(); // it must be safe to delete 'this' in a slot
00479         return;
00480         }
00481     return;
00482     }
00483 
00484 Atom KSelectionWatcher::Private::manager_atom = None;
00485 
00486 #include "kmanagerselection.moc"
00487 #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
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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