00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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
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
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 );
00135 d->timestamp = ev.xproperty.time;
00136 XSelectInput( dpy, d->window, StructureNotifyMask );
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
00142 XDestroyWindow( dpy, d->window );
00143 d->timestamp = CurrentTime;
00144 return false;
00145 }
00146 if( prev_owner != None )
00147 {
00148
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 };
00156 select( 0, NULL, NULL, NULL, &tm );
00157 if( cnt == 19 )
00158 {
00159 if( force_kill_P )
00160 {
00161 KXErrorHandler err;
00162
00163 XKillClient( dpy, prev_owner );
00164 err.error( true );
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
00182 return true;
00183 }
00184
00185
00186 void KSelectionOwner::release()
00187 {
00188 if( d->timestamp == CurrentTime )
00189 return;
00190 XDestroyWindow( QX11Info::display(), d->window );
00191
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
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
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
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;
00264
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;
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 )
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
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 ;
00332 else
00333 return false;
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
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
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
00432 XSelectInput( dpy, RootWindow( dpy, d->screen ), event_mask | StructureNotifyMask );
00433 }
00434 owner();
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
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
00459 void KSelectionWatcher::filterEvent( XEvent* ev_P )
00460 {
00461 if( ev_P->type == ClientMessage )
00462 {
00463
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
00468 if( static_cast< long >( owner()) == ev_P->xclient.data.l[ 2 ] )
00469 {
00470
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;
00479 if( owner() == None )
00480 emit lostOwner();
00481 return;
00482 }
00483 return;
00484 }
00485
00486 Atom KSelectionWatcher::Private::manager_atom = None;
00487
00488 #include "kmanagerselection.moc"
00489 #endif