Git commit a3189d1af16e389032b237c10952675166b171f8 by David Faure. Committed on 30/11/2012 at 19:20. Pushed by dfaure into branch 'frameworks'. Full port to XCB by Fredrik H=C3=B6glund M +437 -266 kdeui/util/kmanagerselection.cpp M +55 -26 kdeui/util/kmanagerselection.h http://commits.kde.org/kdelibs/a3189d1af16e389032b237c10952675166b171f8 diff --git a/kdeui/util/kmanagerselection.cpp b/kdeui/util/kmanagerselectio= n.cpp index 9c356db..4e67549 100644 --- a/kdeui/util/kmanagerselection.cpp +++ b/kdeui/util/kmanagerselection.cpp @@ -39,13 +39,38 @@ DEALINGS IN THE SOFTWARE. #endif = #include +#include +#include = #include #include #include -#include -#include -#include + +static xcb_window_t get_selection_owner(xcb_connection_t *c, xcb_atom_t se= lection) +{ + xcb_window_t owner =3D XCB_NONE; + xcb_get_selection_owner_reply_t *reply =3D xcb_get_selection_owner_rep= ly(c, xcb_get_selection_owner(c, selection), 0); + + if (reply) { + owner =3D reply->owner; + free(reply); + } + + return owner; +} + +static xcb_atom_t intern_atom(xcb_connection_t *c, const char *name) +{ + xcb_atom_t atom =3D XCB_NONE; + xcb_intern_atom_reply_t *reply =3D xcb_intern_atom_reply(c, xcb_intern= _atom(c, false, strlen(name), name), 0); + + if (reply) { + atom =3D reply->atom; + free(reply); + } + + return atom; +} = class KSelectionOwner::Private #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) @@ -53,29 +78,42 @@ class KSelectionOwner::Private #endif { public: - Private( KSelectionOwner* owner_P, Atom selection_P, int screen_P ) - : selection( selection_P ), - screen( screen_P >=3D 0 ? screen_P : DefaultScreen( QX11Info::di= splay() ) ), - window( None ), - timestamp( CurrentTime ), - extra1( 0 ), - extra2( 0 ), - owner( owner_P ) + enum State { Idle, WaitingForTimestamp, WaitingForPreviousOwner }; + + Private(KSelectionOwner* owner_P, xcb_atom_t selection_P, int screen_P) + : state(Idle), + selection(selection_P), + root(QX11Info::appRootWindow(screen_P)), + window(XCB_NONE), + prev_owner(XCB_NONE), + timestamp(XCB_CURRENT_TIME), + extra1(0), + extra2(0), + force_kill(false), + owner(owner_P) { #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) QCoreApplication::instance()->installNativeEventFilter(this); #endif } = - const Atom selection; - const int screen; - Window window; - Time timestamp; - long extra1, extra2; - static Atom manager_atom; - static Atom xa_multiple; - static Atom xa_targets; - static Atom xa_timestamp; + void claimSucceeded(); + void gotTimestamp(); + void timeout(); + + State state; + const xcb_atom_t selection; + xcb_window_t root; + xcb_window_t window; + xcb_window_t prev_owner; + xcb_timestamp_t timestamp; + uint32_t extra1, extra2; + QBasicTimer timer; + bool force_kill; + static xcb_atom_t manager_atom; + static xcb_atom_t xa_multiple; + static xcb_atom_t xa_targets; + static xcb_atom_t xa_timestamp; = protected: bool nativeEventFilter(const QByteArray& eventType, void *message, lon= g *result) Q_DECL_OVERRIDE @@ -92,15 +130,15 @@ private: }; = = -KSelectionOwner::KSelectionOwner( Atom selection_P, int screen_P, QObject*= parent_P ) - : QObject( parent_P ), - d( new Private( this, selection_P, screen_P ) ) +KSelectionOwner::KSelectionOwner(xcb_atom_t selection_P, int screen_P, QOb= ject *parent_P) + : QObject(parent_P), + d(new Private(this, selection_P, screen_P)) { } = -KSelectionOwner::KSelectionOwner( const char* selection_P, int screen_P, Q= Object* parent_P ) - : QObject( parent_P ), - d( new Private( this, XInternAtom( QX11Info::display(), selection_= P, False ), screen_P ) ) +KSelectionOwner::KSelectionOwner(const char *selection_P, int screen_P, QO= bject *parent_P) + : QObject(parent_P), + d(new Private(this, intern_atom(QX11Info::connection(), selection_= P), screen_P)) { } = @@ -110,110 +148,157 @@ KSelectionOwner::~KSelectionOwner() delete d; } = -bool KSelectionOwner::claim( bool force_P, bool force_kill_P ) +void KSelectionOwner::Private::claimSucceeded() +{ + state =3D Idle; + + xcb_client_message_event_t ev; + ev.response_type =3D XCB_CLIENT_MESSAGE; + ev.format =3D 32; + ev.window =3D root; + ev.type =3D Private::manager_atom; + ev.data.data32[0] =3D timestamp; + ev.data.data32[1] =3D selection; + ev.data.data32[2] =3D window; + ev.data.data32[3] =3D extra1; + ev.data.data32[4] =3D extra2; + + xcb_send_event(QX11Info::connection(), false, root, XCB_EVENT_MASK_STR= UCTURE_NOTIFY, (const char *) &ev); + + // kDebug() << "Claimed selection"; + + emit owner->claimedOwnership(); +} + +void KSelectionOwner::Private::gotTimestamp() +{ + Q_ASSERT(state =3D=3D WaitingForTimestamp); + + state =3D Idle; + + xcb_connection_t *c =3D QX11Info::connection(); + + // Set the selection owner and immediately verify that the claim was s= uccessful + xcb_set_selection_owner(c, window, selection, timestamp); + xcb_window_t new_owner =3D get_selection_owner(c, selection); + + if (new_owner !=3D window) { - if( Private::manager_atom =3D=3D None ) + // kDebug() << "Failed to claim selection : " << new_owner; + xcb_destroy_window(c, window); + timestamp =3D XCB_CURRENT_TIME; + window =3D XCB_NONE; + + emit owner->failedToClaimOwnership(); + return; + } + + if (prev_owner !=3D XCB_NONE) { + // kDebug() << "Waiting for previous owner to disown"; + timer.start(1000, owner); + state =3D WaitingForPreviousOwner; + + // Note: We've already selected for structure notify events + // on the previous owner window + return; + } + + // If there was no previous owner, we're done + claimSucceeded(); +} + +void KSelectionOwner::Private::timeout() +{ + Q_ASSERT(state =3D=3D WaitingForPreviousOwner); + + state =3D Idle; + + if (force_kill) { + // kDebug() << "Killing previous owner"; + xcb_connection_t *c =3D QX11Info::connection(); + + // Ignore any errors from the kill request + xcb_generic_error_t *err =3D xcb_request_check(c, xcb_kill_client_= checked(c, prev_owner)); + free(err); + + claimSucceeded(); + return; + } + + emit owner->failedToClaimOwnership(); +} + +void KSelectionOwner::claim(bool force_P, bool force_kill_P) +{ + Q_ASSERT(d->state =3D=3D Private::Idle); + + if (Private::manager_atom =3D=3D XCB_NONE) getAtoms(); - if( d->timestamp !=3D CurrentTime ) + + if (d->timestamp !=3D XCB_CURRENT_TIME) release(); - Display* const dpy =3D QX11Info::display(); - XFlush(dpy); - Window prev_owner =3D XGetSelectionOwner( dpy, d->selection ); - if( prev_owner !=3D None ) - { - if( !force_P ) - { -// kDebug() << "Selection already owned, failing"; - return false; - } - XSelectInput( dpy, prev_owner, StructureNotifyMask ); - } - XSetWindowAttributes attrs; - attrs.override_redirect =3D True; - d->window =3D XCreateWindow( dpy, RootWindow( dpy, d->screen ), 0, 0, = 1, 1, - 0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, = &attrs ); -// kDebug() << "Using owner window " << window; - Atom tmp =3D XA_ATOM; - XSelectInput( dpy, d->window, PropertyChangeMask ); - XChangeProperty( dpy, d->window, XA_ATOM, XA_ATOM, 32, PropModeReplace, - reinterpret_cast< unsigned char* >( &tmp ), 1 ); - XEvent ev; - XSync( dpy, False ); - XCheckTypedWindowEvent( dpy, d->window, PropertyNotify, &ev ); // get = a timestamp - d->timestamp =3D ev.xproperty.time; - XSelectInput( dpy, d->window, StructureNotifyMask ); // for DestroyNot= ify - XSetSelectionOwner( dpy, d->selection, d->window, d->timestamp ); - Window new_owner =3D XGetSelectionOwner( dpy, d->selection ); - if( new_owner !=3D d->window ) - { -// kDebug() << "Failed to claim selection : " << new_owner; - XDestroyWindow( dpy, d->window ); - XFlush(dpy); - d->timestamp =3D CurrentTime; - return false; - } - if( prev_owner !=3D None ) + + xcb_connection_t *c =3D QX11Info::connection(); + d->prev_owner =3D get_selection_owner(c, d->selection); + + if (d->prev_owner !=3D XCB_NONE) + { + if (!force_P) { -// kDebug() << "Waiting for previous owner to disown"; - for( int cnt =3D 0; - ; - ++cnt ) - { - if( XCheckTypedWindowEvent( dpy, prev_owner, DestroyNotify, &e= v ) =3D=3D True ) - break; - struct timeval tm =3D { 0, 50000 }; // 50 ms - select( 0, NULL, NULL, NULL, &tm ); - if( cnt =3D=3D 19 ) - { - if( force_kill_P ) - { - KXErrorHandler err; -// kDebug() << "Killing previous owner"; - XKillClient( dpy, prev_owner ); - err.error( true ); // ignore errors when killing - } - break; - } - } + // kDebug() << "Selection already owned, failing"; + emit failedToClaimOwnership(); + return; } - ev.type =3D ClientMessage; - ev.xclient.window =3D RootWindow( dpy, d->screen ); - ev.xclient.display =3D dpy; - ev.xclient.message_type =3D Private::manager_atom; - ev.xclient.format =3D 32; - ev.xclient.data.l[ 0 ] =3D d->timestamp; - ev.xclient.data.l[ 1 ] =3D d->selection; - ev.xclient.data.l[ 2 ] =3D d->window; - ev.xclient.data.l[ 3 ] =3D d->extra1; - ev.xclient.data.l[ 4 ] =3D d->extra2; - XSendEvent( dpy, RootWindow( dpy, d->screen ), False, StructureNotifyM= ask, &ev ); - XFlush(dpy); -// kDebug() << "Claimed selection"; - return true; + + // Select structure notify events so get an event when the previou= s owner + // destroys the window + uint32_t mask =3D XCB_EVENT_MASK_STRUCTURE_NOTIFY; + xcb_change_window_attributes(c, d->prev_owner, XCB_CW_EVENT_MASK, = &mask); } = + uint32_t values[] =3D { true, XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVE= NT_MASK_STRUCTURE_NOTIFY }; + + d->window =3D xcb_generate_id(c); + xcb_create_window(c, XCB_COPY_FROM_PARENT, d->window, d->root, 0, 0, 1= , 1, 0, + XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, + XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK, values= ); + + // Trigger a property change event so we get a timestamp + xcb_atom_t tmp =3D XCB_ATOM_ATOM; + xcb_change_property(c, XCB_PROP_MODE_REPLACE, d->window, XCB_ATOM_ATOM= , XCB_ATOM_ATOM, 32, 1, (const void *) &tmp); + + // Now we have to return to the event loop and wait for the property c= hange event + d->force_kill =3D force_kill_P; + d->state =3D Private::WaitingForTimestamp; +} + // destroy resource first void KSelectionOwner::release() - { - if( d->timestamp =3D=3D CurrentTime ) +{ + if (d->timestamp =3D=3D XCB_CURRENT_TIME) return; - XDestroyWindow( QX11Info::display(), d->window ); // also makes the se= lection not owned -// kDebug() << "Releasing selection"; - d->timestamp =3D CurrentTime; - } = -Window KSelectionOwner::ownerWindow() const - { - if( d->timestamp =3D=3D CurrentTime ) - return None; + xcb_destroy_window(QX11Info::connection(), d->window); // also makes t= he selection not owned + d->window =3D XCB_NONE; + + // kDebug() << "Releasing selection"; + + d->timestamp =3D XCB_CURRENT_TIME; +} + +xcb_window_t KSelectionOwner::ownerWindow() const +{ + if (d->timestamp =3D=3D XCB_CURRENT_TIME) + return XCB_NONE; + return d->window; - } +} = -void KSelectionOwner::setData( long extra1_P, long extra2_P ) - { +void KSelectionOwner::setData(uint32_t extra1_P, uint32_t extra2_P) +{ d->extra1 =3D extra1_P; d->extra2 =3D extra2_P; - } +} = bool KSelectionOwner::filterEvent( void* ev_P ) { @@ -229,27 +314,44 @@ bool KSelectionOwner::filterEvent( void* ev_P ) return true; } #endif - switch( response_type ) + switch (response_type) { case XCB_SELECTION_CLEAR: { xcb_selection_clear_event_t* ev =3D reinterpret_cast(event); - if( d->timestamp =3D=3D CurrentTime || ev->selection !=3D d->selec= tion ) + if( d->timestamp =3D=3D XCB_CURRENT_TIME || ev->selection !=3D d->= selection ) return false; - d->timestamp =3D CurrentTime; + + d->timestamp =3D XCB_CURRENT_TIME; // kDebug() << "Lost selection"; - Window window =3D d->window; + + xcb_window_t window =3D d->window; emit lostOwnership(); - XSelectInput( QX11Info::display(), window, 0 ); - XDestroyWindow( QX11Info::display(), window ); + + // Unset the event mask before we destroy the window so we don't g= et a destroy event + uint32_t event_mask =3D XCB_NONE; + xcb_change_window_attributes(QX11Info::connection(), window, XCB_C= W_EVENT_MASK, &event_mask); + xcb_destroy_window(QX11Info::connection(), window); return true; } case XCB_DESTROY_NOTIFY: { xcb_destroy_notify_event_t* ev =3D reinterpret_cast(event); - if( d->timestamp =3D=3D CurrentTime || ev->window !=3D d->window ) + if (ev->window =3D=3D d->prev_owner) { + if (d->state =3D=3D Private::WaitingForPreviousOwner) { + d->timer.stop(); + d->claimSucceeded(); + return true; + } + // It is possible for the previous owner to be destroyed + // while we're waiting for the timestamp + d->prev_owner =3D XCB_NONE; + } + + if (d->timestamp =3D=3D XCB_CURRENT_TIME || ev->window !=3D d->win= dow) return false; - d->timestamp =3D CurrentTime; + + d->timestamp =3D XCB_CURRENT_TIME; // kDebug() << "Lost selection (destroyed)"; emit lostOwnership(); return true; @@ -257,16 +359,39 @@ bool KSelectionOwner::filterEvent( void* ev_P ) case XCB_SELECTION_NOTIFY: { xcb_selection_notify_event_t* ev =3D reinterpret_cast(event); - if( d->timestamp =3D=3D CurrentTime || ev->selection !=3D d->selec= tion ) + if( d->timestamp =3D=3D XCB_CURRENT_TIME || ev->selection !=3D d->= selection ) return false; + // ignore? return false; } case XCB_SELECTION_REQUEST: filter_selection_request(event); return false; + case XCB_PROPERTY_NOTIFY: + { + xcb_property_notify_event_t *ev =3D reinterpret_cast(event); + if (ev->window =3D=3D d->window && d->state =3D=3D Private::Waitin= gForTimestamp) { + d->timestamp =3D ev->time; + d->gotTimestamp(); + return true; + } + return false; } - return false; + default: + return false; + } +} + +void KSelectionOwner::timerEvent(QTimerEvent *event) +{ + if (event->timerId() =3D=3D d->timer.timerId()) { + d->timer.stop(); + d->timeout(); + return; + } + + QObject::timerEvent(event); } = #if 0 @@ -280,116 +405,137 @@ void KSelectionOwner::filter_selection_request( voi= d* event ) { xcb_selection_request_event_t* ev =3D reinterpret_cast(event); = - if( d->timestamp =3D=3D CurrentTime || ev->selection !=3D d->selection= ) + if (d->timestamp =3D=3D XCB_CURRENT_TIME || ev->selection !=3D d->sele= ction) return; - if( ev->time !=3D CurrentTime - && ev->time - d->timestamp > 1U << 31 ) + + if (ev->time !=3D XCB_CURRENT_TIME && ev->time - d->timestamp > 1U << = 31) return; // too old or too new request -// kDebug() << "Got selection request"; + + // kDebug() << "Got selection request"; + + xcb_connection_t *c =3D QX11Info::connection(); bool handled =3D false; - if( ev->target =3D=3D Private::xa_multiple ) + + if (ev->target =3D=3D Private::xa_multiple) + { + if (ev->property !=3D XCB_NONE) { - if( ev->property !=3D None ) - { - const int MAX_ATOMS =3D 100; // no need to handle more? - int format; - Atom type; - unsigned long items; - unsigned long after; - unsigned char* data; - if( XGetWindowProperty( QX11Info::display(), ev->requestor, ev= ->property, 0, - MAX_ATOMS, False, AnyPropertyType, &type, &format, &items,= &after, - &data ) =3D=3D Success && format =3D=3D 32 && items % 2 = =3D=3D 0 ) - { - bool handled_array[ MAX_ATOMS ]; - Atom* atoms =3D reinterpret_cast< Atom* >( data ); - for( unsigned int i =3D 0; - i < items / 2; - ++i ) - handled_array[ i ] =3D handle_selection( - atoms[ i * 2 ], atoms[ i * 2 + 1 ], ev->requestor = ); + const int MAX_ATOMS =3D 100; + + xcb_get_property_cookie_t cookie =3D xcb_get_property(c, false= , ev->requestor, ev->property, + XCB_GET_PR= OPERTY_TYPE_ANY, 0, MAX_ATOMS); + xcb_get_property_reply_t *reply =3D xcb_get_property_reply(c, = cookie, 0); + + if (reply && reply->format =3D=3D 32 && reply->value_len % 2 = =3D=3D 0) { + xcb_atom_t *atoms =3D reinterpret_cast(xcb_g= et_property_value(reply)); + bool handled_array[MAX_ATOMS]; + + for (int i =3D 0; i < reply->value_len / 2; i++) + handled_array[i] =3D handle_selection(atoms[i * 2], at= oms[i * 2 + 1], ev->requestor); + bool all_handled =3D true; - for( unsigned int i =3D 0; - i < items / 2; - ++i ) - if( !handled_array[ i ] ) - { + for (int i =3D 0; i < reply->value_len / 2; i++) { + if (!handled_array[i]) { all_handled =3D false; - atoms[ i * 2 + 1 ] =3D None; - } - if( !all_handled ) - XChangeProperty( QX11Info::display(), ev->requestor, e= v->property, XA_ATOM, - 32, PropModeReplace, reinterpret_cast< unsigned ch= ar* >( atoms ), items ); - handled =3D true; - XFree( data ); + atoms[i * 2 + 1] =3D XCB_NONE; + } + } + + if (!all_handled) { + xcb_change_property(c, ev->requestor, ev->property, XC= B_ATOM_ATOM, 32, XCB_PROP_MODE_REPLACE, + reply->value_len, reinterpret_cast= (atoms)); } + + handled =3D true; } + + if (reply) + free(reply); } - else - { - if( ev->property =3D=3D None ) // obsolete client + } else { + if (ev->property =3D=3D XCB_NONE) // obsolete client ev->property =3D ev->target; - handled =3D handle_selection( ev->target, ev->property, ev->reques= tor ); - } - XEvent xev; - xev.xselection.selection =3D ev->selection; - xev.xselection.type =3D SelectionNotify; - xev.xselection.display =3D QX11Info::display(); - xev.xselection.requestor =3D ev->requestor; - xev.xselection.target =3D ev->target; - xev.xselection.property =3D handled ? ev->property : None; - XSendEvent( QX11Info::display(), ev->requestor, False, 0, &xev ); + + handled =3D handle_selection(ev->target, ev->property, ev->request= or); } = -bool KSelectionOwner::handle_selection( Atom target_P, Atom property_P, Wi= ndow requestor_P ) - { - if( target_P =3D=3D Private::xa_timestamp ) - { -// kDebug() << "Handling timestamp request"; - XChangeProperty( QX11Info::display(), requestor_P, property_P, XA_= INTEGER, 32, - PropModeReplace, reinterpret_cast< unsigned char* >( &d->times= tamp ), 1 ); - } - else if( target_P =3D=3D Private::xa_targets ) - replyTargets( property_P, requestor_P ); - else if( genericReply( target_P, property_P, requestor_P )) - ; // handled - else + xcb_selection_notify_event_t xev; + xev.response_type =3D XCB_SELECTION_NOTIFY; + xev.selection =3D ev->selection; + xev.requestor =3D ev->requestor; + xev.target =3D ev->target; + xev.property =3D handled ? ev->property : XCB_NONE; + + xcb_send_event(c, false, ev->requestor, 0, (const char *) &xev); +} + +bool KSelectionOwner::handle_selection(xcb_atom_t target_P, xcb_atom_t pro= perty_P, xcb_window_t requestor_P) +{ + if( target_P =3D=3D Private::xa_timestamp ) { + // kDebug() << "Handling timestamp request"; + xcb_change_property(QX11Info::connection(), requestor_P, property_= P, XCB_ATOM_INTEGER, 32, + XCB_PROP_MODE_REPLACE, 1, reinterpret_cast(&d->timestamp)); + } else if (target_P =3D=3D Private::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 ] =3D { Private::xa_multiple, Private::xa_timestamp, Pri= vate::xa_targets }; -// kDebug() << "Handling targets request"; - XChangeProperty( QX11Info::display(), requestor_P, property_P, XA_ATOM= , 32, PropModeReplace, - reinterpret_cast< unsigned char* >( atoms ), 3 ); - } + return true; +} = -bool KSelectionOwner::genericReply( Atom, Atom, Window ) - { +void KSelectionOwner::replyTargets(xcb_atom_t property_P, xcb_window_t req= uestor_P) +{ + xcb_atom_t atoms[3] =3D { Private::xa_multiple, Private::xa_timestamp,= Private::xa_targets }; + + xcb_change_property(QX11Info::connection(), requestor_P, property_P, X= CB_ATOM_ATOM, 32, XCB_PROP_MODE_REPLACE, + sizeof(atoms) / sizeof(atoms[0]), reinterpret_cast= (atoms)); + + // kDebug() << "Handling targets request"; +} + +bool KSelectionOwner::genericReply(xcb_atom_t, xcb_atom_t, xcb_window_t) +{ return false; - } +} = void KSelectionOwner::getAtoms() - { - if( Private::manager_atom =3D=3D None ) - { - Atom atoms[ 4 ]; - const char* const names[] =3D - { "MANAGER", "MULTIPLE", "TARGETS", "TIMESTAMP" }; - XInternAtoms( QX11Info::display(), const_cast< char** >( names ), = 4, False, atoms ); - Private::manager_atom =3D atoms[ 0 ]; - Private::xa_multiple =3D atoms[ 1]; - Private::xa_targets =3D atoms[ 2 ]; - Private::xa_timestamp =3D atoms[ 3 ]; +{ + if (Private::manager_atom !=3D XCB_NONE) + return; + + xcb_connection_t *c =3D QX11Info::connection(); + + struct { + const char *name; + xcb_atom_t *atom; + } atoms[] =3D { + { "MANAGER", &Private::manager_atom }, + { "MULTIPLE", &Private::xa_multiple }, + { "TARGETS", &Private::xa_targets }, + { "TIMESTAMP", &Private::xa_timestamp } + }; + + const int count =3D sizeof(atoms) / sizeof(atoms[0]); + xcb_intern_atom_cookie_t cookies[count]; + + for (int i =3D 0; i < count; i++) + cookies[i] =3D xcb_intern_atom(c, false, strlen(atoms[i].name), at= oms[i].name); + + for (int i =3D 0; i < count; i++) { + if (xcb_intern_atom_reply_t *reply =3D xcb_intern_atom_reply(c, co= okies[i], 0)) { + *atoms[i].atom =3D reply->atom; + free(reply); } } +} = -Atom KSelectionOwner::Private::manager_atom =3D None; -Atom KSelectionOwner::Private::xa_multiple =3D None; -Atom KSelectionOwner::Private::xa_targets =3D None; -Atom KSelectionOwner::Private::xa_timestamp =3D None; +xcb_atom_t KSelectionOwner::Private::manager_atom =3D XCB_NONE; +xcb_atom_t KSelectionOwner::Private::xa_multiple =3D XCB_NONE; +xcb_atom_t KSelectionOwner::Private::xa_targets =3D XCB_NONE; +xcb_atom_t KSelectionOwner::Private::xa_timestamp =3D XCB_NONE; = //******************************************* // KSelectionWatcher @@ -402,21 +548,21 @@ class KSelectionWatcher::Private #endif { public: - Private( KSelectionWatcher* watcher_P, Atom selection_P, int screen_P ) - : selection( selection_P ), - screen( screen_P >=3D 0 ? screen_P : DefaultScreen( QX11Info::di= splay())), - selection_owner( None ), - watcher( watcher_P ) + Private(KSelectionWatcher* watcher_P, xcb_atom_t selection_P, int scre= en_P) + : root(QX11Info::appRootWindow(screen_P)), + selection(selection_P), + selection_owner(XCB_NONE), + watcher(watcher_P) { #if QT_VERSION > QT_VERSION_CHECK(5, 0, 0) QCoreApplication::instance()->installNativeEventFilter(this); #endif } = - const Atom selection; - const int screen; - Window selection_owner; - static Atom manager_atom; + xcb_window_t root; + const xcb_atom_t selection; + xcb_window_t selection_owner; + static xcb_atom_t manager_atom; = protected: bool nativeEventFilter(const QByteArray& eventType, void *message, lon= g *result) Q_DECL_OVERRIDE @@ -432,62 +578,85 @@ private: KSelectionWatcher* watcher; }; = -KSelectionWatcher::KSelectionWatcher( Atom selection_P, int screen_P, QObj= ect* parent_P ) - : QObject( parent_P ), - d( new Private( this, selection_P, screen_P )) - { +KSelectionWatcher::KSelectionWatcher(xcb_atom_t selection_P, int screen_P,= QObject *parent_P) + : QObject(parent_P), + d(new Private(this, selection_P, screen_P)) +{ init(); - } +} = -KSelectionWatcher::KSelectionWatcher( const char* selection_P, int screen_= P, QObject* parent_P ) - : QObject( parent_P ), - d( new Private( this, XInternAtom( QX11Info::display(), selection_= P, False ), screen_P )) - { +KSelectionWatcher::KSelectionWatcher(const char *selection_P, int screen_P= , QObject *parent_P) + : QObject(parent_P), + d(new Private(this, intern_atom(QX11Info::connection(), selection_= P), screen_P)) +{ init(); - } +} = KSelectionWatcher::~KSelectionWatcher() - { +{ delete d; - } +} = void KSelectionWatcher::init() +{ + if (Private::manager_atom =3D=3D XCB_NONE) { - if( Private::manager_atom =3D=3D None ) - { - Display* const dpy =3D QX11Info::display(); - Private::manager_atom =3D XInternAtom( dpy, "MANAGER", False ); - XWindowAttributes attrs; - XGetWindowAttributes( dpy, RootWindow( dpy, d->screen ), &attrs ); - long event_mask =3D attrs.your_event_mask; - // StructureNotifyMask on the root window is needed - XSelectInput( dpy, RootWindow( dpy, d->screen ), event_mask | Stru= ctureNotifyMask ); - XFlush(dpy); + xcb_connection_t *c =3D QX11Info::connection(); + + xcb_intern_atom_cookie_t atom_cookie =3D xcb_intern_atom(c, false,= strlen("MANAGER"), "MANAGER"); + xcb_get_window_attributes_cookie_t attr_cookie =3D xcb_get_window_= attributes(c, d->root); + + xcb_intern_atom_reply_t *atom_reply =3D xcb_intern_atom_reply(c, a= tom_cookie, 0); + Private::manager_atom =3D atom_reply->atom; + free(atom_reply); + + xcb_get_window_attributes_reply_t *attr =3D xcb_get_window_attribu= tes_reply(c, attr_cookie, 0); + uint32_t event_mask =3D attr->your_event_mask; + free(attr); + + if (!(event_mask & XCB_EVENT_MASK_STRUCTURE_NOTIFY)) { + // We need XCB_EVENT_MASK_STRUCTURE_NORITY on the root window + event_mask |=3D XCB_EVENT_MASK_STRUCTURE_NOTIFY; + xcb_change_window_attributes(c, d->root, XCB_CW_EVENT_MASK, &e= vent_mask); } - owner(); // trigger reading of current selection status } = -Window KSelectionWatcher::owner() - { - Display* const dpy =3D QX11Info::display(); - KXErrorHandler handler; - Window current_owner =3D XGetSelectionOwner( dpy, d->selection ); - if( current_owner =3D=3D None ) - return None; - if( current_owner =3D=3D d->selection_owner ) + owner(); // trigger reading of current selection status +} + +xcb_window_t KSelectionWatcher::owner() +{ + xcb_connection_t *c =3D QX11Info::connection(); + + xcb_window_t current_owner =3D get_selection_owner(c, d->selection); + if (current_owner =3D=3D XCB_NONE) + return XCB_NONE; + + if (current_owner =3D=3D d->selection_owner) return d->selection_owner; - XSelectInput( dpy, current_owner, StructureNotifyMask ); - if( !handler.error( true ) && current_owner =3D=3D XGetSelectionOwner(= dpy, d->selection )) - { -// kDebug() << "isOwner: " << current_owner; + + // We have a new selection owner - select for structure notify events + uint32_t mask =3D XCB_EVENT_MASK_STRUCTURE_NOTIFY; + xcb_void_cookie_t cookie =3D xcb_change_window_attributes_checked(c, c= urrent_owner, XCB_CW_EVENT_MASK, &mask); + + // Verify that the owner didn't change again while selecting for events + xcb_window_t new_owner =3D get_selection_owner(c, d->selection); + xcb_generic_error_t *err =3D xcb_request_check(c, cookie); + + if (!err && current_owner =3D=3D new_owner) { d->selection_owner =3D current_owner; - emit newOwner( d->selection_owner ); - } - else - d->selection_owner =3D None; - return d->selection_owner; + emit newOwner(d->selection_owner); + } else { + // ### This doesn't look right - the selection could have an owner + d->selection_owner =3D XCB_NONE; } = + if (err) + free(err); + + return d->selection_owner; +} + void KSelectionWatcher::filterEvent(void* ev_P) { xcb_generic_event_t* event =3D reinterpret_cast= (ev_P); @@ -506,13 +675,15 @@ void KSelectionWatcher::filterEvent(void* ev_P) // } if (response_type =3D=3D XCB_DESTROY_NOTIFY) { xcb_destroy_notify_event_t* ev =3D reinterpret_cast(event); - if( d->selection_owner =3D=3D None || ev->window !=3D d->selection= _owner ) + if( d->selection_owner =3D=3D XCB_NONE || ev->window !=3D d->selec= tion_owner ) return; - d->selection_owner =3D None; // in case the exactly same ID gets r= eused as the owner - if( owner() =3D=3D None ) + + d->selection_owner =3D XCB_NONE; // in case the exactly same ID ge= ts reused as the owner + + if (owner() =3D=3D XCB_NONE) emit lostOwner(); // it must be safe to delete 'this' in a slot return; } } = -Atom KSelectionWatcher::Private::manager_atom =3D None; +xcb_atom_t KSelectionWatcher::Private::manager_atom =3D XCB_NONE; diff --git a/kdeui/util/kmanagerselection.h b/kdeui/util/kmanagerselection.h index 21ace13..e931e26 100644 --- a/kdeui/util/kmanagerselection.h +++ b/kdeui/util/kmanagerselection.h @@ -28,9 +28,8 @@ DEALINGS IN THE SOFTWARE. #include #include = -#include -#include - +#include +#include = /** This class implements claiming and owning manager selections, as described @@ -53,7 +52,8 @@ class KDEUI_EXPORT KSelectionOwner * @param screen X screen, or -1 for default * @param parent parent object, or NULL if there is none */ - explicit KSelectionOwner( Atom selection, int screen =3D -1, QObje= ct* parent =3D NULL ); + explicit KSelectionOwner(xcb_atom_t selection, int screen =3D -1, = QObject* parent =3D NULL); + /** * @overload * This constructor accepts the selection name and creates the app= ropriate atom @@ -63,35 +63,50 @@ class KDEUI_EXPORT KSelectionOwner * @param screen X screen, or -1 for default * @param parent parent object, or NULL if there is none */ - explicit KSelectionOwner( const char* selection, int screen =3D -1= , QObject* parent =3D NULL ); + explicit KSelectionOwner(const char *selection, int screen =3D -1,= QObject* parent =3D NULL); + /** * Destructor. Calls release(). */ virtual ~KSelectionOwner(); + /** - * This function attemps to claim ownership of the manager selecti= on, using - * the current X timestamp. If @p force is false, and the selectio= n is already - * owned, the selection is not claimed, and false is returned. If = claiming - * is forced and the selection is owned by another client, it is w= aited for up to 1 second - * for the previous owner to disown the selection, if @p force_kil= l is true, - * and the previous owner fails to disown the selection in time, - * it will be forcibly killed. True is returned after successfully= claiming - * ownership of the selection. + * Try to claim ownership of the manager selection using the curre= nt X timestamp. + * + * This function returns immediately, but it may take up to one se= cond for the claim + * to succeed or fail, at which point either the claimedOwnership(= ) or + * failedToClaimOwnership() signal is emitted. The claim will not = be completed until + * the caller has returned to the event loop. + * + * If @p force is false, and the selection is already owned, the s= election is not claimed, + * and failedToClaimOwnership() is emitted. If @p force is true an= d the selection is + * owned by another client, the client will be given one second to= relinquish ownership + * of the selection. If @p force_kill is true, and the previous ow= ner fails to disown + * the selection in time, it will be forcibly killed. */ - bool claim( bool force, bool force_kill =3D true ); + void claim(bool force, bool force_kill =3D true); + /** * If the selection is owned, the ownership is given up. */ void release(); + /** * If the selection is owned, returns the window used internally * for owning the selection. */ - Window ownerWindow() const; // None if not owning the selection + xcb_window_t ownerWindow() const; // None if not owning the select= ion + + /** + * @internal + */ + bool filterEvent(void *ev_P); // internal + /** * @internal */ - bool filterEvent( void* ev_P ); // internal + void timerEvent(QTimerEvent *event); + Q_SIGNALS: /** * This signal is emitted if the selection was owned and the owner= ship @@ -100,6 +115,19 @@ class KDEUI_EXPORT KSelectionOwner * to this signal. */ void lostOwnership(); + + /** + * This signal is emitted when claim() was succesful in claiming + * ownership of the selection. + */ + void claimedOwnership(); + + /** + * This signal is emitted when claim() failed to claim ownership + * of the selection. + */ + void failedToClaimOwnership(); + protected: /** * Called for every X event received on the window used for owning @@ -115,13 +143,13 @@ class KDEUI_EXPORT KSelectionOwner * @param property property to use for the reply data * @param requestor requestor window */ - virtual bool genericReply( Atom target, Atom property, Window requ= estor ); + virtual bool genericReply(xcb_atom_t target, xcb_atom_t property, = xcb_window_t requestor); /** * Called to announce the supported targets, as described in the I= CCCM * section 2.6. The default implementation announces the required = targets * MULTIPLE, TIMESTAMP and TARGETS. */ - virtual void replyTargets( Atom property, Window requestor ); + virtual void replyTargets(xcb_atom_t property, xcb_window_t reques= tor); /** * Called to create atoms needed for claiming the selection and * communication using the selection handling mechanism. The defau= lt @@ -134,10 +162,11 @@ class KDEUI_EXPORT KSelectionOwner * after successfully claiming a selection. These extra data * are in data.l[3] and data.l[4] fields of the XClientMessage. */ - void setData( long extra1, long extra2 ); + void setData(uint32_t extra1, uint32_t extra2); + private: - void filter_selection_request( void* ev_P ); - bool handle_selection( Atom target_P, Atom property_P, Window requ= estor_P ); + void filter_selection_request(void *ev_P); + bool handle_selection(xcb_atom_t target_P, xcb_atom_t property_P, = xcb_window_t requestor_P); = class Private; Private* const d; @@ -163,7 +192,7 @@ class KDEUI_EXPORT KSelectionWatcher * @param screen X screen, or -1 for default * @param parent parent object, or NULL if there is none */ - explicit KSelectionWatcher( Atom selection, int screen =3D -1, QOb= ject* parent =3D NULL ); + explicit KSelectionWatcher(xcb_atom_t selection, int screen =3D -1= , QObject *parent =3D NULL); /** * @overload * This constructor accepts the selection name and creates the app= ropriate atom @@ -173,25 +202,25 @@ class KDEUI_EXPORT KSelectionWatcher * @param screen X screen, or -1 for default * @param parent parent object, or NULL if there is none */ - explicit KSelectionWatcher( const char* selection, int screen =3D = -1, QObject* parent =3D NULL ); + explicit KSelectionWatcher(const char *selection, int screen =3D -= 1, QObject *parent =3D NULL); virtual ~KSelectionWatcher(); /** * Return the current owner of the manager selection, if any. Note= that if the event * informing about the owner change is still in the input queue, n= ewOwner() might * have been emitted yet. */ - Window owner(); + xcb_window_t owner(); /** * @internal */ - void filterEvent( void* ev_P ); // internal + void filterEvent(void *ev_P); // internal Q_SIGNALS: /** * This signal is emitted when the selection is successfully claim= ed by a new * owner. * @param owner the new owner of the selection */ - void newOwner( Window owner ); + void newOwner(xcb_window_t owner); /** * This signal is emitted when the selection is given up, i.e. the= re's no * owner. Note that the selection may be immediatelly claimed agai= n,