1 // X11 implementation of clipboard access for Nasal
3 // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * See the following links for more information on X11 clipboard:
22 * http://www.jwz.org/doc/x-cut-and-paste.html
23 * http://michael.toren.net/mirrors/doc/X-copy+paste.txt
24 * https://github.com/kfish/xsel/blob/master/xsel.c
27 #include "NasalClipboard.hxx"
29 #include <simgear/debug/logstream.hxx>
32 #include <X11/Xatom.h>
39 _display( XOpenDisplay(NULL) ),
40 _window( XCreateSimpleWindow(
42 DefaultRootWindow(_display),
43 0, 0, 1, 1, // dummy dimensions -> window will never be mapped
47 _atom_targets( XInternAtom(_display, "TARGETS", False) ),
48 _atom_text( XInternAtom(_display, "TEXT", False) ),
49 _atom_utf8( XInternAtom(_display, "UTF8_STRING", False) ),
50 _atom_primary( XInternAtom(_display, "PRIMARY", False) ),
51 _atom_clipboard( XInternAtom(_display, "CLIPBOARD", False) )
56 virtual ~ClipboardX11()
58 // Ensure we get rid of any selection ownership
59 if( XGetSelectionOwner(_display, _atom_primary) )
60 XSetSelectionOwner(_display, _atom_primary, None, CurrentTime);
61 if( XGetSelectionOwner(_display, _atom_clipboard) )
62 XSetSelectionOwner(_display, _atom_clipboard, None, CurrentTime);
66 * We need to run an event queue to check for selection request
70 while( XPending(_display) )
73 XNextEvent(_display, &event);
79 * Get clipboard contents as text
81 virtual std::string getText(Type type)
83 Atom atom_type = typeToAtom(type);
85 //Request a list of possible conversions
86 XConvertSelection( _display, atom_type, _atom_targets, atom_type,
87 _window, CurrentTime );
89 Atom requested_type = None;
90 bool sent_request = false;
92 for(int cnt = 0; cnt < 5;)
95 XNextEvent(_display, &event);
97 if( event.type != SelectionNotify )
103 Atom target = event.xselection.target;
104 if(event.xselection.property == None)
106 if( target == _atom_targets )
107 // If TARGETS can not be converted no selection is available
114 "ClipboardX11::getText: Conversion failed: "
115 "target=" << getAtomName(target)
120 //If we're being given a list of targets (possible conversions)
121 if(target == _atom_targets && !sent_request)
124 requested_type = XA_STRING; // TODO select other type
125 XConvertSelection( _display, atom_type, requested_type, atom_type,
126 _window, CurrentTime );
128 else if(target == requested_type)
130 Property prop = readProperty(_window, atom_type);
131 if( prop.format != 8 )
137 "ClipboardX11::getText: can only handle 8-bit data (is "
138 << prop.format << "-bit) -> retry "
145 std::string result((const char*)prop.data, prop.num_items);
156 "ClipboardX11::getText: wrong target: " << getAtomName(target)
162 return std::string();
166 * Set clipboard contents as text
168 virtual bool setText(const std::string& text, Type type)
170 Atom atom_type = typeToAtom(type);
171 XSetSelectionOwner(_display, atom_type, _window, CurrentTime);
172 if( XGetSelectionOwner(_display, atom_type) != _window )
178 "ClipboardX11::setText: failed to get selection owner!"
183 // We need to store the text for sending it to another application upon
185 if( type == CLIPBOARD )
203 std::string _clipboard,
206 void handleEvent(const XEvent& event)
210 case SelectionRequest:
211 handleSelectionRequest(event.xselectionrequest);
214 if( event.xselectionclear.selection == _atom_clipboard )
224 "ClipboardX11: unexpected XEvent: " << event.type
230 void handleSelectionRequest(const XSelectionRequestEvent& sel_req)
236 "ClipboardX11: handle selection request: "
237 "selection=" << getAtomName(sel_req.selection) << ", "
238 "target=" << getAtomName(sel_req.target)
241 const std::string& buf = sel_req.selection == _atom_clipboard
245 // Prepare response to notify whether we have written to the property or
246 // are unable to do the conversion
247 XSelectionEvent response;
248 response.type = SelectionNotify;
249 response.display = sel_req.display;
250 response.requestor = sel_req.requestor;
251 response.selection = sel_req.selection;
252 response.target = sel_req.target;
253 response.property = sel_req.property;
254 response.time = sel_req.time;
256 if( sel_req.target == _atom_targets )
258 static Atom supported[] = {
273 else if( sel_req.target == XA_STRING
274 || sel_req.target == _atom_text
275 || sel_req.target == _atom_utf8 )
288 // Notify requestor that we are unable to perform requested conversion
289 response.property = None;
292 XSendEvent(_display, sel_req.requestor, False, 0, (XEvent*)&response);
295 void changeProperty( Window w,
303 _display, w, prop, type, 8, PropModeReplace,
304 static_cast<const unsigned char*>(data), size
312 unsigned long num_items;
316 // Get all data from a property
317 Property readProperty(Window w, Atom property)
321 unsigned long nitems;
322 unsigned long bytes_after;
323 unsigned char *ret = 0;
325 int read_bytes = 1024;
327 //Keep trying to read the property until there are no
336 _display, w, property, 0, read_bytes, False, AnyPropertyType,
337 &actual_type, &actual_format, &nitems, &bytes_after,
342 } while( bytes_after );
344 Property p = {ret, actual_format, nitems, actual_type};
348 std::string getAtomName(Atom atom) const
350 return atom == None ? "None" : XGetAtomName(_display, atom);
353 Atom typeToAtom(Type type) const
355 return type == CLIPBOARD ? _atom_clipboard : _atom_primary;
360 //------------------------------------------------------------------------------
361 NasalClipboard::Ptr NasalClipboard::create()
363 return NasalClipboard::Ptr(new ClipboardX11);