• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KDEUI

  • kdeui
  • util
kmanagerselection.cpp
Go to the documentation of this file.
1/****************************************************************************
2
3 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
4
5Permission is hereby granted, free of charge, to any person obtaining a
6copy of this software and associated documentation files (the "Software"),
7to deal in the Software without restriction, including without limitation
8the rights to use, copy, modify, merge, publish, distribute, sublicense,
9and/or sell copies of the Software, and to permit persons to whom the
10Software is furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21DEALINGS IN THE SOFTWARE.
22
23****************************************************************************/
24
25#include "kmanagerselection.h"
26
27#include <config.h>
28
29#ifdef HAVE_SYS_TYPES_H
30#include <sys/types.h>
31#endif
32
33#ifdef HAVE_SYS_TIME_H
34#include <sys/time.h>
35#endif
36
37#ifdef HAVE_UNISTD_H
38#include <unistd.h>
39#endif
40
41#include <QtCore/QObject>
42#ifdef Q_WS_X11 // FIXME(E)
43
44#include <qx11info_x11.h>
45#include <qwidget.h>
46#include <kdebug.h>
47#include <kapplication.h>
48#include <kxerrorhandler.h>
49#include <X11/Xatom.h>
50
51
52class KSelectionOwner::Private : public QWidget
53{
54public:
55 Private( KSelectionOwner* owner_P, Atom selection_P, int screen_P )
56 : selection( selection_P ),
57 screen( screen_P >= 0 ? screen_P : DefaultScreen( QX11Info::display() ) ),
58 window( None ),
59 timestamp( CurrentTime ),
60 extra1( 0 ),
61 extra2( 0 ),
62 owner( owner_P )
63 {
64 kapp->installX11EventFilter( this );
65 }
66
67 const Atom selection;
68 const int screen;
69 Window window;
70 Time timestamp;
71 long extra1, extra2;
72 static Atom manager_atom;
73 static Atom xa_multiple;
74 static Atom xa_targets;
75 static Atom xa_timestamp;
76
77protected:
78 virtual bool x11Event( XEvent* ev_P )
79 {
80 return owner->filterEvent( ev_P );
81 }
82
83private:
84 KSelectionOwner* owner;
85};
86
87
88KSelectionOwner::KSelectionOwner( Atom selection_P, int screen_P, QObject* parent_P )
89 : QObject( parent_P ),
90 d( new Private( this, selection_P, screen_P ) )
91{
92}
93
94KSelectionOwner::KSelectionOwner( const char* selection_P, int screen_P, QObject* parent_P )
95 : QObject( parent_P ),
96 d( new Private( this, XInternAtom( QX11Info::display(), selection_P, False ), screen_P ) )
97{
98}
99
100KSelectionOwner::~KSelectionOwner()
101{
102 release();
103 delete d;
104}
105
106bool KSelectionOwner::claim( bool force_P, bool force_kill_P )
107 {
108 if( Private::manager_atom == None )
109 getAtoms();
110 if( d->timestamp != CurrentTime )
111 release();
112 Display* const dpy = QX11Info::display();
113 Window prev_owner = XGetSelectionOwner( dpy, d->selection );
114 if( prev_owner != None )
115 {
116 if( !force_P )
117 {
118// kDebug() << "Selection already owned, failing";
119 return false;
120 }
121 XSelectInput( dpy, prev_owner, StructureNotifyMask );
122 }
123 XSetWindowAttributes attrs;
124 attrs.override_redirect = True;
125 d->window = XCreateWindow( dpy, RootWindow( dpy, d->screen ), 0, 0, 1, 1,
126 0, CopyFromParent, InputOnly, CopyFromParent, CWOverrideRedirect, &attrs );
127// kDebug() << "Using owner window " << window;
128 Atom tmp = XA_ATOM;
129 XSelectInput( dpy, d->window, PropertyChangeMask );
130 XChangeProperty( dpy, d->window, XA_ATOM, XA_ATOM, 32, PropModeReplace,
131 reinterpret_cast< unsigned char* >( &tmp ), 1 );
132 XEvent ev;
133 XSync( dpy, False );
134 XCheckTypedWindowEvent( dpy, d->window, PropertyNotify, &ev ); // get a timestamp
135 d->timestamp = ev.xproperty.time;
136 XSelectInput( dpy, d->window, StructureNotifyMask ); // for DestroyNotify
137 XSetSelectionOwner( dpy, d->selection, d->window, d->timestamp );
138 Window new_owner = XGetSelectionOwner( dpy, d->selection );
139 if( new_owner != d->window )
140 {
141// kDebug() << "Failed to claim selection : " << new_owner;
142 XDestroyWindow( dpy, d->window );
143 d->timestamp = CurrentTime;
144 return false;
145 }
146 if( prev_owner != None )
147 {
148// kDebug() << "Waiting for previous owner to disown";
149 for( int cnt = 0;
150 ;
151 ++cnt )
152 {
153 if( XCheckTypedWindowEvent( dpy, prev_owner, DestroyNotify, &ev ) == True )
154 break;
155 struct timeval tm = { 0, 50000 }; // 50 ms
156 select( 0, NULL, NULL, NULL, &tm );
157 if( cnt == 19 )
158 {
159 if( force_kill_P )
160 {
161 KXErrorHandler err;
162// kDebug() << "Killing previous owner";
163 XKillClient( dpy, prev_owner );
164 err.error( true ); // ignore errors when killing
165 }
166 break;
167 }
168 }
169 }
170 ev.type = ClientMessage;
171 ev.xclient.window = RootWindow( dpy, d->screen );
172 ev.xclient.display = dpy;
173 ev.xclient.message_type = Private::manager_atom;
174 ev.xclient.format = 32;
175 ev.xclient.data.l[ 0 ] = d->timestamp;
176 ev.xclient.data.l[ 1 ] = d->selection;
177 ev.xclient.data.l[ 2 ] = d->window;
178 ev.xclient.data.l[ 3 ] = d->extra1;
179 ev.xclient.data.l[ 4 ] = d->extra2;
180 XSendEvent( dpy, RootWindow( dpy, d->screen ), False, StructureNotifyMask, &ev );
181// kDebug() << "Claimed selection";
182 return true;
183 }
184
185// destroy resource first
186void KSelectionOwner::release()
187 {
188 if( d->timestamp == CurrentTime )
189 return;
190 XDestroyWindow( QX11Info::display(), d->window ); // also makes the selection not owned
191// kDebug() << "Releasing selection";
192 d->timestamp = CurrentTime;
193 }
194
195Window KSelectionOwner::ownerWindow() const
196 {
197 if( d->timestamp == CurrentTime )
198 return None;
199 return d->window;
200 }
201
202void KSelectionOwner::setData( long extra1_P, long extra2_P )
203 {
204 d->extra1 = extra1_P;
205 d->extra2 = extra2_P;
206 }
207
208bool KSelectionOwner::filterEvent( XEvent* ev_P )
209 {
210 if( d->timestamp != CurrentTime && ev_P->xany.window == d->window )
211 {
212 if( handleMessage( ev_P ))
213 return true;
214 }
215 switch( ev_P->type )
216 {
217 case SelectionClear:
218 {
219 if( d->timestamp == CurrentTime || ev_P->xselectionclear.selection != d->selection )
220 return false;
221 d->timestamp = CurrentTime;
222// kDebug() << "Lost selection";
223 Window window = d->window;
224 emit lostOwnership();
225 XSelectInput( QX11Info::display(), window, 0 );
226 XDestroyWindow( QX11Info::display(), window );
227 return true;
228 }
229 case DestroyNotify:
230 {
231 if( d->timestamp == CurrentTime || ev_P->xdestroywindow.window != d->window )
232 return false;
233 d->timestamp = CurrentTime;
234// kDebug() << "Lost selection (destroyed)";
235 emit lostOwnership();
236 return true;
237 }
238 case SelectionNotify:
239 {
240 if( d->timestamp == CurrentTime || ev_P->xselection.selection != d->selection )
241 return false;
242 // ignore?
243 return false;
244 }
245 case SelectionRequest:
246 filter_selection_request( ev_P->xselectionrequest );
247 return false;
248 }
249 return false;
250 }
251
252bool KSelectionOwner::handleMessage( XEvent* )
253 {
254 return false;
255 }
256
257void KSelectionOwner::filter_selection_request( XSelectionRequestEvent& ev_P )
258 {
259 if( d->timestamp == CurrentTime || ev_P.selection != d->selection )
260 return;
261 if( ev_P.time != CurrentTime
262 && ev_P.time - d->timestamp > 1U << 31 )
263 return; // too old or too new request
264// kDebug() << "Got selection request";
265 bool handled = false;
266 if( ev_P.target == Private::xa_multiple )
267 {
268 if( ev_P.property != None )
269 {
270 const int MAX_ATOMS = 100; // no need to handle more?
271 int format;
272 Atom type;
273 unsigned long items;
274 unsigned long after;
275 unsigned char* data;
276 if( XGetWindowProperty( QX11Info::display(), ev_P.requestor, ev_P.property, 0,
277 MAX_ATOMS, False, AnyPropertyType, &type, &format, &items, &after,
278 &data ) == Success && format == 32 && items % 2 == 0 )
279 {
280 bool handled_array[ MAX_ATOMS ];
281 Atom* atoms = reinterpret_cast< Atom* >( data );
282 for( unsigned int i = 0;
283 i < items / 2;
284 ++i )
285 handled_array[ i ] = handle_selection(
286 atoms[ i * 2 ], atoms[ i * 2 + 1 ], ev_P.requestor );
287 bool all_handled = true;
288 for( unsigned int i = 0;
289 i < items / 2;
290 ++i )
291 if( !handled_array[ i ] )
292 {
293 all_handled = false;
294 atoms[ i * 2 + 1 ] = None;
295 }
296 if( !all_handled )
297 XChangeProperty( QX11Info::display(), ev_P.requestor, ev_P.property, XA_ATOM,
298 32, PropModeReplace, reinterpret_cast< unsigned char* >( atoms ), items );
299 handled = true;
300 XFree( data );
301 }
302 }
303 }
304 else
305 {
306 if( ev_P.property == None ) // obsolete client
307 ev_P.property = ev_P.target;
308 handled = handle_selection( ev_P.target, ev_P.property, ev_P.requestor );
309 }
310 XEvent ev;
311 ev.xselection.selection = ev_P.selection;
312 ev.xselection.type = SelectionNotify;
313 ev.xselection.display = QX11Info::display();
314 ev.xselection.requestor = ev_P.requestor;
315 ev.xselection.target = ev_P.target;
316 ev.xselection.property = handled ? ev_P.property : None;
317 XSendEvent( QX11Info::display(), ev_P.requestor, False, 0, &ev );
318 }
319
320bool KSelectionOwner::handle_selection( Atom target_P, Atom property_P, Window requestor_P )
321 {
322 if( target_P == Private::xa_timestamp )
323 {
324// kDebug() << "Handling timestamp request";
325 XChangeProperty( QX11Info::display(), requestor_P, property_P, XA_INTEGER, 32,
326 PropModeReplace, reinterpret_cast< unsigned char* >( &d->timestamp ), 1 );
327 }
328 else if( target_P == Private::xa_targets )
329 replyTargets( property_P, requestor_P );
330 else if( genericReply( target_P, property_P, requestor_P ))
331 ; // handled
332 else
333 return false; // unknown
334 return true;
335 }
336
337void KSelectionOwner::replyTargets( Atom property_P, Window requestor_P )
338 {
339 Atom atoms[ 3 ] = { Private::xa_multiple, Private::xa_timestamp, Private::xa_targets };
340// kDebug() << "Handling targets request";
341 XChangeProperty( QX11Info::display(), requestor_P, property_P, XA_ATOM, 32, PropModeReplace,
342 reinterpret_cast< unsigned char* >( atoms ), 3 );
343 }
344
345bool KSelectionOwner::genericReply( Atom, Atom, Window )
346 {
347 return false;
348 }
349
350void KSelectionOwner::getAtoms()
351 {
352 if( Private::manager_atom == None )
353 {
354 Atom atoms[ 4 ];
355 const char* const names[] =
356 { "MANAGER", "MULTIPLE", "TARGETS", "TIMESTAMP" };
357 XInternAtoms( QX11Info::display(), const_cast< char** >( names ), 4, False, atoms );
358 Private::manager_atom = atoms[ 0 ];
359 Private::xa_multiple = atoms[ 1];
360 Private::xa_targets = atoms[ 2 ];
361 Private::xa_timestamp = atoms[ 3 ];
362 }
363 }
364
365Atom KSelectionOwner::Private::manager_atom = None;
366Atom KSelectionOwner::Private::xa_multiple = None;
367Atom KSelectionOwner::Private::xa_targets = None;
368Atom KSelectionOwner::Private::xa_timestamp = None;
369
370//*******************************************
371// KSelectionWatcher
372//*******************************************
373
374
375class KSelectionWatcher::Private : public QWidget
376{
377public:
378 Private( KSelectionWatcher* watcher_P, Atom selection_P, int screen_P )
379 : selection( selection_P ),
380 screen( screen_P >= 0 ? screen_P : DefaultScreen( QX11Info::display())),
381 selection_owner( None ),
382 watcher( watcher_P )
383 {
384 kapp->installX11EventFilter( this );
385 }
386
387 const Atom selection;
388 const int screen;
389 Window selection_owner;
390 static Atom manager_atom;
391
392protected:
393 virtual bool x11Event( XEvent* ev_P )
394 {
395 watcher->filterEvent( ev_P );
396 return false;
397 }
398
399private:
400 KSelectionWatcher* watcher;
401};
402
403KSelectionWatcher::KSelectionWatcher( Atom selection_P, int screen_P, QObject* parent_P )
404 : QObject( parent_P ),
405 d( new Private( this, selection_P, screen_P ))
406 {
407 init();
408 }
409
410KSelectionWatcher::KSelectionWatcher( const char* selection_P, int screen_P, QObject* parent_P )
411 : QObject( parent_P ),
412 d( new Private( this, XInternAtom( QX11Info::display(), selection_P, False ), screen_P ))
413 {
414 init();
415 }
416
417KSelectionWatcher::~KSelectionWatcher()
418 {
419 delete d;
420 }
421
422void KSelectionWatcher::init()
423 {
424 if( Private::manager_atom == None )
425 {
426 Display* const dpy = QX11Info::display();
427 Private::manager_atom = XInternAtom( dpy, "MANAGER", False );
428 XWindowAttributes attrs;
429 XGetWindowAttributes( dpy, RootWindow( dpy, d->screen ), &attrs );
430 long event_mask = attrs.your_event_mask;
431 // StructureNotifyMask on the root window is needed
432 XSelectInput( dpy, RootWindow( dpy, d->screen ), event_mask | StructureNotifyMask );
433 }
434 owner(); // trigger reading of current selection status
435 }
436
437Window KSelectionWatcher::owner()
438 {
439 Display* const dpy = QX11Info::display();
440 KXErrorHandler handler;
441 Window current_owner = XGetSelectionOwner( dpy, d->selection );
442 if( current_owner == None )
443 return None;
444 if( current_owner == d->selection_owner )
445 return d->selection_owner;
446 XSelectInput( dpy, current_owner, StructureNotifyMask );
447 if( !handler.error( true ) && current_owner == XGetSelectionOwner( dpy, d->selection ))
448 {
449// kDebug() << "isOwner: " << current_owner;
450 d->selection_owner = current_owner;
451 emit newOwner( d->selection_owner );
452 }
453 else
454 d->selection_owner = None;
455 return d->selection_owner;
456 }
457
458// void return value in order to allow more watchers in one process
459void KSelectionWatcher::filterEvent( XEvent* ev_P )
460 {
461 if( ev_P->type == ClientMessage )
462 {
463// kDebug() << "got ClientMessage";
464 if( ev_P->xclient.message_type != Private::manager_atom
465 || ev_P->xclient.data.l[ 1 ] != static_cast< long >( d->selection ))
466 return;
467// kDebug() << "handling message";
468 if( static_cast< long >( owner()) == ev_P->xclient.data.l[ 2 ] )
469 {
470 // owner() emits newOwner() if needed, no need to do it twice
471 }
472 return;
473 }
474 if( ev_P->type == DestroyNotify )
475 {
476 if( d->selection_owner == None || ev_P->xdestroywindow.window != d->selection_owner )
477 return;
478 d->selection_owner = None; // in case the exactly same ID gets reused as the owner
479 if( owner() == None )
480 emit lostOwner(); // it must be safe to delete 'this' in a slot
481 return;
482 }
483 return;
484 }
485
486Atom KSelectionWatcher::Private::manager_atom = None;
487
488#include "kmanagerselection.moc"
489#endif
KSelectionOwner
This class implements claiming and owning manager selections, as described in the ICCCM,...
Definition: kmanagerselection.h:47
KSelectionOwner::handleMessage
virtual bool handleMessage(XEvent *ev)
Called for every X event received on the window used for owning the selection.
Definition: kmanagerselection.cpp:252
KSelectionOwner::~KSelectionOwner
virtual ~KSelectionOwner()
Destructor.
Definition: kmanagerselection.cpp:100
KSelectionOwner::release
void release()
If the selection is owned, the ownership is given up.
Definition: kmanagerselection.cpp:186
KSelectionOwner::filterEvent
bool filterEvent(XEvent *ev_P)
Definition: kmanagerselection.cpp:208
KSelectionOwner::replyTargets
virtual void replyTargets(Atom property, Window requestor)
Called to announce the supported targets, as described in the ICCCM section 2.6.
Definition: kmanagerselection.cpp:337
KSelectionOwner::ownerWindow
Window ownerWindow() const
If the selection is owned, returns the window used internally for owning the selection.
Definition: kmanagerselection.cpp:195
KSelectionOwner::setData
void setData(long extra1, long extra2)
Sets extra data to be sent in the message sent to root window after successfully claiming a selection...
Definition: kmanagerselection.cpp:202
KSelectionOwner::KSelectionOwner
KSelectionOwner(Atom selection, int screen=-1, QObject *parent=NULL)
This constructor initializes the object, but doesn't perform any operation on the selection.
Definition: kmanagerselection.cpp:88
KSelectionOwner::genericReply
virtual bool genericReply(Atom target, Atom property, Window requestor)
Called when a SelectionRequest event is received.
Definition: kmanagerselection.cpp:345
KSelectionOwner::lostOwnership
void lostOwnership()
This signal is emitted if the selection was owned and the ownership has been lost due to another clie...
KSelectionOwner::getAtoms
virtual void getAtoms()
Called to create atoms needed for claiming the selection and communication using the selection handli...
Definition: kmanagerselection.cpp:350
KSelectionOwner::claim
bool claim(bool force, bool force_kill=true)
This function attemps to claim ownership of the manager selection, using the current X timestamp.
Definition: kmanagerselection.cpp:106
KSelectionWatcher
This class implements watching manager selections, as described in the ICCCM section 2....
Definition: kmanagerselection.h:157
KSelectionWatcher::KSelectionWatcher
KSelectionWatcher(Atom selection, int screen=-1, QObject *parent=NULL)
This constructor initializes the object, but doesn't perform any operation on the selection.
Definition: kmanagerselection.cpp:403
KSelectionWatcher::filterEvent
void filterEvent(XEvent *ev_P)
Definition: kmanagerselection.cpp:459
KSelectionWatcher::owner
Window owner()
Return the current owner of the manager selection, if any.
Definition: kmanagerselection.cpp:437
KSelectionWatcher::newOwner
void newOwner(Window owner)
This signal is emitted when the selection is successfully claimed by a new owner.
KSelectionWatcher::~KSelectionWatcher
virtual ~KSelectionWatcher()
Definition: kmanagerselection.cpp:417
KSelectionWatcher::lostOwner
void lostOwner()
This signal is emitted when the selection is given up, i.e.
KXErrorHandler
This class simplifies handling of X errors.
Definition: kxerrorhandler.h:63
KXErrorHandler::error
bool error(bool sync) const
This function returns true if the error flag is set (i.e.
Definition: kxerrorhandler.cpp:99
QObject
QWidget
kapplication.h
kapp
#define kapp
Definition: kapplication.h:56
Atom
unsigned long Atom
Definition: kapplication.h:40
kdebug.h
kmanagerselection.h
kxerrorhandler.h
Window
Window
None
None
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

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

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal