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

KDEUI

  • sources
  • kde-4.14
  • kdelibs
  • kdeui
  • util
kmanagerselection.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 
3  Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
4 
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
11 
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 DEALINGS 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 
52 class KSelectionOwner::Private : public QWidget
53 {
54 public:
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 
77 protected:
78  virtual bool x11Event( XEvent* ev_P )
79  {
80  return owner->filterEvent( ev_P );
81  }
82 
83 private:
84  KSelectionOwner* owner;
85 };
86 
87 
88 KSelectionOwner::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 
94 KSelectionOwner::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 
100 KSelectionOwner::~KSelectionOwner()
101 {
102  release();
103  delete d;
104 }
105 
106 bool 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
186 void 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 
195 Window KSelectionOwner::ownerWindow() const
196  {
197  if( d->timestamp == CurrentTime )
198  return None;
199  return d->window;
200  }
201 
202 void KSelectionOwner::setData( long extra1_P, long extra2_P )
203  {
204  d->extra1 = extra1_P;
205  d->extra2 = extra2_P;
206  }
207 
208 bool 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 
252 bool KSelectionOwner::handleMessage( XEvent* )
253  {
254  return false;
255  }
256 
257 void 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 
320 bool 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 
337 void 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 
345 bool KSelectionOwner::genericReply( Atom, Atom, Window )
346  {
347  return false;
348  }
349 
350 void 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 
365 Atom KSelectionOwner::Private::manager_atom = None;
366 Atom KSelectionOwner::Private::xa_multiple = None;
367 Atom KSelectionOwner::Private::xa_targets = None;
368 Atom KSelectionOwner::Private::xa_timestamp = None;
369 
370 //*******************************************
371 // KSelectionWatcher
372 //*******************************************
373 
374 
375 class KSelectionWatcher::Private : public QWidget
376 {
377 public:
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 
392 protected:
393  virtual bool x11Event( XEvent* ev_P )
394  {
395  watcher->filterEvent( ev_P );
396  return false;
397  }
398 
399 private:
400  KSelectionWatcher* watcher;
401 };
402 
403 KSelectionWatcher::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 
410 KSelectionWatcher::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 
417 KSelectionWatcher::~KSelectionWatcher()
418  {
419  delete d;
420  }
421 
422 void 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 
437 Window 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
459 void 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 
486 Atom KSelectionWatcher::Private::manager_atom = None;
487 
488 #include "kmanagerselection.moc"
489 #endif
Atom
unsigned long Atom
Definition: kapplication.h:29
kapp
#define kapp
Definition: kapplication.h:56
QWidget
KXErrorHandler::error
bool error(bool sync) const
This function returns true if the error flag is set (i.e.
Definition: kxerrorhandler.cpp:99
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
kdebug.h
kapplication.h
KSelectionOwner::~KSelectionOwner
virtual ~KSelectionOwner()
Destructor.
Definition: kmanagerselection.cpp:100
QWidget::window
QWidget * window() const
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
This class implements claiming and owning manager selections, as described in the ICCCM...
Definition: kmanagerselection.h:45
KSelectionOwner::filterEvent
bool filterEvent(XEvent *ev_P)
Definition: kmanagerselection.cpp:208
QX11Info::display
Display * display()
KSelectionWatcher::owner
Window owner()
Return the current owner of the manager selection, if any.
Definition: kmanagerselection.cpp:437
KSelectionOwner::release
void release()
If the selection is owned, the ownership is given up.
Definition: kmanagerselection.cpp:186
QWidget::x11Event
virtual bool x11Event(XEvent *event)
KSelectionWatcher::filterEvent
void filterEvent(XEvent *ev_P)
Definition: kmanagerselection.cpp:459
KSelectionWatcher::newOwner
void newOwner(Window owner)
This signal is emitted when the selection is successfully claimed by a new owner. ...
KSelectionOwner::lostOwnership
void lostOwnership()
This signal is emitted if the selection was owned and the ownership has been lost due to another clie...
KSelectionWatcher::lostOwner
void lostOwner()
This signal is emitted when the selection is given up, i.e.
None
QObject
KSelectionWatcher
This class implements watching manager selections, as described in the ICCCM section 2...
Definition: kmanagerselection.h:155
KXErrorHandler
This class simplifies handling of X errors.
Definition: kxerrorhandler.h:62
kmanagerselection.h
kxerrorhandler.h
KSelectionOwner::genericReply
virtual bool genericReply(Atom target, Atom property, Window requestor)
Called when a SelectionRequest event is received.
Definition: kmanagerselection.cpp:345
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
KSelectionOwner::getAtoms
virtual void getAtoms()
Called to create atoms needed for claiming the selection and communication using the selection handli...
Definition: kmanagerselection.cpp:350
Window
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
KSelectionWatcher::~KSelectionWatcher
virtual ~KSelectionWatcher()
Definition: kmanagerselection.cpp:417
KSelectionOwner::ownerWindow
Window ownerWindow() const
If the selection is owned, returns the window used internally for owning the selection.
Definition: kmanagerselection.cpp:195
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
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
QX11Info
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:23:59 by doxygen 1.8.7 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 API Reference

Skip menu "kdelibs 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

Search



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