]> git.mxchange.org Git - flightgear.git/blob - src/Scripting/ClipboardX11.cxx
6108c52425ab260384561adade4bb14643625038
[flightgear.git] / src / Scripting / ClipboardX11.cxx
1 // X11 implementation of clipboard access for Nasal
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
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.
9 //
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.
14 //
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.
18
19 #include "NasalClipboard.hxx"
20
21 #include <simgear/debug/logstream.hxx>
22
23 #include <X11/Xlib.h>
24 #include <X11/Xatom.h>
25
26 class ClipboardX11:
27   public NasalClipboard
28 {
29   public:
30     ClipboardX11():
31       _display( XOpenDisplay(NULL) ),
32       _window( XCreateSimpleWindow(
33         _display,
34         DefaultRootWindow(_display),
35         0, 0, 1, 1, // dummy dimensions -> window will never be mapped
36         0,
37         0, 0
38       ) ),
39       _atom_targets( XInternAtom(_display, "TARGETS", False) ),
40       _atom_primary( XInternAtom(_display, "PRIMARY", False) ),
41       _atom_clipboard( XInternAtom(_display, "CLIPBOARD", False) )
42     {
43       assert(_display);
44       assert(_atom_targets != None);
45       assert(_atom_primary != None);
46       assert(_atom_clipboard != None);
47     }
48
49     /**
50      * Get clipboard contents as text
51      */
52     virtual std::string getText(Type type)
53     {
54       Atom atom_type = (type == CLIPBOARD ? _atom_clipboard : _atom_primary);
55
56       //Request a list of possible conversions
57       XConvertSelection( _display, atom_type, _atom_targets, atom_type,
58                          _window, CurrentTime );
59       XFlush(_display);
60
61       Atom requested_type = None;
62       bool sent_request = false;
63
64       for(int cnt = 0; cnt < 5;)
65       {
66         XEvent event;
67         XNextEvent(_display, &event);
68
69         if( event.type == SelectionNotify )
70         {
71           Atom target = event.xselection.target;
72           if(event.xselection.property == None)
73           {
74             if( target == _atom_targets )
75               // If TARGETS can not be converted no selection is available
76               break;
77
78             SG_LOG
79             (
80               SG_NASAL,
81               SG_WARN,
82               "ClipboardX11::getText: Conversion failed: "
83                                      "target=" << getAtomName(target)
84             );
85             break;
86           }
87           else
88           {
89             //If we're being given a list of targets (possible conversions)
90             if(target == _atom_targets && !sent_request)
91             {
92               sent_request = true;
93               requested_type = XA_STRING; // TODO select other type
94               XConvertSelection( _display, atom_type, requested_type, atom_type,
95                                  _window, CurrentTime );
96             }
97             else if(target == requested_type)
98             {
99               Property prop = readProperty(_window, atom_type);
100               if( prop.format != 8 )
101               {
102                 SG_LOG
103                 (
104                   SG_NASAL,
105                   SG_WARN,
106                   "ClipboardX11::getText: can only handle 8-bit data (is "
107                                        << prop.format << "-bit) -> retry "
108                                        << cnt++
109                 );
110                 XFree(prop.data);
111                 continue;
112               }
113
114               std::string result((const char*)prop.data, prop.num_items);
115               XFree(prop.data);
116
117               return result;
118             }
119             else
120             {
121               SG_LOG
122               (
123                 SG_NASAL,
124                 SG_WARN,
125                 "ClipboardX11::getText: wrong target: " << getAtomName(target)
126               );
127               break;
128             }
129           }
130         }
131         else
132         {
133           SG_LOG
134           (
135             SG_NASAL,
136             SG_WARN,
137             "ClipboardX11::getText: unexpected XEvent: " << event.type
138           );
139           break;
140         }
141       }
142
143       return std::string();
144     }
145
146     /**
147      * Set clipboard contents as text
148      */
149     virtual bool setText(const std::string& text, Type type)
150     {
151       SG_LOG
152       (
153         SG_NASAL,
154         SG_ALERT,
155         "ClipboardX11::setText: not yet implemented!"
156       );
157       return false;
158     }
159
160   protected:
161
162     Display    *_display;
163     Window      _window;
164     Atom        _atom_targets,
165                 _atom_primary,
166                 _atom_clipboard;
167
168     struct Property
169     {
170       unsigned char *data;
171       int format, num_items;
172       Atom type;
173     };
174
175     // Get all data from a property
176     Property readProperty(Window w, Atom property)
177     {
178       Atom actual_type;
179       int actual_format;
180       unsigned long nitems;
181       unsigned long bytes_after;
182       unsigned char *ret=0;
183
184       int read_bytes = 1024;
185
186       //Keep trying to read the property until there are no
187       //bytes unread.
188       do
189       {
190         if( ret )
191           XFree(ret);
192
193         XGetWindowProperty
194         (
195           _display, w, property, 0, read_bytes, False, AnyPropertyType,
196           &actual_type, &actual_format, &nitems, &bytes_after,
197           &ret
198         );
199
200         read_bytes *= 2;
201       } while( bytes_after );
202
203       Property p = {ret, actual_format, nitems, actual_type};
204       return p;
205     }
206
207     std::string getAtomName(Atom atom)
208     {
209       return atom == None ? "None" : XGetAtomName(_display, atom);
210     }
211
212 };
213
214 //------------------------------------------------------------------------------
215 NasalClipboard::Ptr NasalClipboard::create()
216 {
217   return NasalClipboard::Ptr(new ClipboardX11);
218 }