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
KDE 4.6 API Reference