]> git.mxchange.org Git - flightgear.git/blobdiff - src/Scripting/ClipboardX11.cxx
Remove debug console output in FGApproachController
[flightgear.git] / src / Scripting / ClipboardX11.cxx
index 6108c52425ab260384561adade4bb14643625038..c8747863b74521e90cac3601da2a0ea95d82ad38 100644 (file)
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
+/*
+ * See the following links for more information on X11 clipboard:
+ *
+ * http://www.jwz.org/doc/x-cut-and-paste.html
+ * http://michael.toren.net/mirrors/doc/X-copy+paste.txt
+ * https://github.com/kfish/xsel/blob/master/xsel.c
+ */
+
 #include "NasalClipboard.hxx"
 
 #include <simgear/debug/logstream.hxx>
@@ -37,13 +45,34 @@ class ClipboardX11:
         0, 0
       ) ),
       _atom_targets( XInternAtom(_display, "TARGETS", False) ),
+      _atom_text( XInternAtom(_display, "TEXT", False) ),
+      _atom_utf8( XInternAtom(_display, "UTF8_STRING", False) ),
       _atom_primary( XInternAtom(_display, "PRIMARY", False) ),
       _atom_clipboard( XInternAtom(_display, "CLIPBOARD", False) )
     {
       assert(_display);
-      assert(_atom_targets != None);
-      assert(_atom_primary != None);
-      assert(_atom_clipboard != None);
+    }
+
+    virtual ~ClipboardX11()
+    {
+      // Ensure we get rid of any selection ownership
+      if( XGetSelectionOwner(_display, _atom_primary) )
+        XSetSelectionOwner(_display, _atom_primary, None, CurrentTime);
+      if( XGetSelectionOwner(_display, _atom_clipboard) )
+        XSetSelectionOwner(_display, _atom_clipboard, None, CurrentTime);
+    }
+
+    /**
+     * We need to run an event queue to check for selection request
+     */
+    virtual void update()
+    {
+      while( XPending(_display) )
+      {
+        XEvent event;
+        XNextEvent(_display, &event);
+        handleEvent(event);
+      }
     }
 
     /**
@@ -51,12 +80,11 @@ class ClipboardX11:
      */
     virtual std::string getText(Type type)
     {
-      Atom atom_type = (type == CLIPBOARD ? _atom_clipboard : _atom_primary);
+      Atom atom_type = typeToAtom(type);
 
       //Request a list of possible conversions
       XConvertSelection( _display, atom_type, _atom_targets, atom_type,
                          _window, CurrentTime );
-      XFlush(_display);
 
       Atom requested_type = None;
       bool sent_request = false;
@@ -66,67 +94,58 @@ class ClipboardX11:
         XEvent event;
         XNextEvent(_display, &event);
 
-        if( event.type == SelectionNotify )
+        if( event.type != SelectionNotify )
         {
-          Atom target = event.xselection.target;
-          if(event.xselection.property == None)
-          {
-            if( target == _atom_targets )
-              // If TARGETS can not be converted no selection is available
-              break;
+          handleEvent(event);
+          continue;
+        }
+
+        Atom target = event.xselection.target;
+        if(event.xselection.property == None)
+        {
+          if( target == _atom_targets )
+            // If TARGETS can not be converted no selection is available
+            break;
+
+          SG_LOG
+          (
+            SG_NASAL,
+            SG_WARN,
+            "ClipboardX11::getText: Conversion failed: "
+                                   "target=" << getAtomName(target)
+          );
+          break;
+        }
 
+        //If we're being given a list of targets (possible conversions)
+        if(target == _atom_targets && !sent_request)
+        {
+          sent_request = true;
+          requested_type = XA_STRING; // TODO select other type
+          XConvertSelection( _display, atom_type, requested_type, atom_type,
+                             _window, CurrentTime );
+        }
+        else if(target == requested_type)
+        {
+          Property prop = readProperty(_window, atom_type);
+          if( prop.format != 8 )
+          {
             SG_LOG
             (
               SG_NASAL,
               SG_WARN,
-              "ClipboardX11::getText: Conversion failed: "
-                                     "target=" << getAtomName(target)
+              "ClipboardX11::getText: can only handle 8-bit data (is "
+                                   << prop.format << "-bit) -> retry "
+                                   << cnt++
             );
-            break;
-          }
-          else
-          {
-            //If we're being given a list of targets (possible conversions)
-            if(target == _atom_targets && !sent_request)
-            {
-              sent_request = true;
-              requested_type = XA_STRING; // TODO select other type
-              XConvertSelection( _display, atom_type, requested_type, atom_type,
-                                 _window, CurrentTime );
-            }
-            else if(target == requested_type)
-            {
-              Property prop = readProperty(_window, atom_type);
-              if( prop.format != 8 )
-              {
-                SG_LOG
-                (
-                  SG_NASAL,
-                  SG_WARN,
-                  "ClipboardX11::getText: can only handle 8-bit data (is "
-                                       << prop.format << "-bit) -> retry "
-                                       << cnt++
-                );
-                XFree(prop.data);
-                continue;
-              }
-
-              std::string result((const char*)prop.data, prop.num_items);
-              XFree(prop.data);
-
-              return result;
-            }
-            else
-            {
-              SG_LOG
-              (
-                SG_NASAL,
-                SG_WARN,
-                "ClipboardX11::getText: wrong target: " << getAtomName(target)
-              );
-              break;
-            }
+            XFree(prop.data);
+            continue;
           }
+
+          std::string result((const char*)prop.data, prop.num_items);
+          XFree(prop.data);
+
+          return result;
         }
         else
         {
@@ -134,7 +153,7 @@ class ClipboardX11:
           (
             SG_NASAL,
             SG_WARN,
-            "ClipboardX11::getText: unexpected XEvent: " << event.type
+            "ClipboardX11::getText: wrong target: " << getAtomName(target)
           );
           break;
         }
@@ -148,13 +167,27 @@ class ClipboardX11:
      */
     virtual bool setText(const std::string& text, Type type)
     {
-      SG_LOG
-      (
-        SG_NASAL,
-        SG_ALERT,
-        "ClipboardX11::setText: not yet implemented!"
-      );
-      return false;
+      Atom atom_type = typeToAtom(type);
+      XSetSelectionOwner(_display, atom_type, _window, CurrentTime);
+      if( XGetSelectionOwner(_display, atom_type) != _window )
+      {
+        SG_LOG
+        (
+          SG_NASAL,
+          SG_ALERT,
+          "ClipboardX11::setText: failed to get selection owner!"
+        );
+        return false;
+      }
+
+      // We need to store the text for sending it to another application upon
+      // request
+      if( type == CLIPBOARD )
+        _clipboard = text;
+      else
+        _selection = text;
+
+      return true;
     }
 
   protected:
@@ -162,13 +195,121 @@ class ClipboardX11:
     Display    *_display;
     Window      _window;
     Atom        _atom_targets,
+                _atom_text,
+                _atom_utf8,
                 _atom_primary,
                 _atom_clipboard;
 
+    std::string _clipboard,
+                _selection;
+
+    void handleEvent(const XEvent& event)
+    {
+      switch( event.type )
+      {
+        case SelectionRequest:
+          handleSelectionRequest(event.xselectionrequest);
+          break;
+        case SelectionClear:
+          if( event.xselectionclear.selection == _atom_clipboard )
+            _clipboard.clear();
+          else
+            _selection.clear();
+          break;
+        default:
+          SG_LOG
+          (
+            SG_NASAL,
+            SG_WARN,
+            "ClipboardX11: unexpected XEvent: " << event.type
+          );
+          break;
+      }
+    }
+
+    void handleSelectionRequest(const XSelectionRequestEvent& sel_req)
+    {
+      SG_LOG
+      (
+        SG_NASAL,
+        SG_DEBUG,
+        "ClipboardX11: handle selection request: "
+                      "selection=" << getAtomName(sel_req.selection) << ", "
+                      "target=" << getAtomName(sel_req.target)
+      );
+
+      const std::string& buf = sel_req.selection == _atom_clipboard
+                             ? _clipboard
+                             : _selection;
+
+      // Prepare response to notify whether we have written to the property or
+      // are unable to do the conversion
+      XSelectionEvent response;
+      response.type = SelectionNotify;
+      response.display = sel_req.display;
+      response.requestor = sel_req.requestor;
+      response.selection = sel_req.selection;
+      response.target = sel_req.target;
+      response.property = sel_req.property;
+      response.time = sel_req.time;
+
+      if( sel_req.target == _atom_targets )
+      {
+        static Atom supported[] = {
+          XA_STRING,
+          _atom_text,
+          _atom_utf8
+        };
+
+        changeProperty
+        (
+          sel_req.requestor,
+          sel_req.property,
+          sel_req.target,
+          &supported,
+          sizeof(supported)
+        );
+      }
+      else if(    sel_req.target == XA_STRING
+               || sel_req.target == _atom_text
+               || sel_req.target == _atom_utf8 )
+      {
+        changeProperty
+        (
+          sel_req.requestor,
+          sel_req.property,
+          sel_req.target,
+          buf.data(),
+          buf.size()
+        );
+      }
+      else
+      {
+        // Notify requestor that we are unable to perform requested conversion
+        response.property = None;
+      }
+
+      XSendEvent(_display, sel_req.requestor, False, 0, (XEvent*)&response);
+    }
+
+    void changeProperty( Window w,
+                         Atom prop,
+                         Atom type,
+                         const void *data,
+                         size_t size )
+    {
+      XChangeProperty
+      (
+        _display, w, prop, type, 8, PropModeReplace,
+        static_cast<const unsigned char*>(data), size
+      );
+    }
+
     struct Property
     {
       unsigned char *data;
-      int format, num_items;
+      int format;
+      unsigned long num_items;
       Atom type;
     };
 
@@ -179,7 +320,7 @@ class ClipboardX11:
       int actual_format;
       unsigned long nitems;
       unsigned long bytes_after;
-      unsigned char *ret=0;
+      unsigned char *ret = 0;
 
       int read_bytes = 1024;
 
@@ -204,11 +345,16 @@ class ClipboardX11:
       return p;
     }
 
-    std::string getAtomName(Atom atom)
+    std::string getAtomName(Atom atom) const
     {
       return atom == None ? "None" : XGetAtomName(_display, atom);
     }
 
+    Atom typeToAtom(Type type) const
+    {
+      return type == CLIPBOARD ? _atom_clipboard : _atom_primary;
+    }
+
 };
 
 //------------------------------------------------------------------------------