]> git.mxchange.org Git - flightgear.git/commitdiff
Basic clipboard access from Nasal.
authorThomas Geymayer <tomgey@gmail.com>
Fri, 3 Aug 2012 22:24:26 +0000 (00:24 +0200)
committerThomas Geymayer <tomgey@gmail.com>
Sat, 4 Aug 2012 15:33:48 +0000 (17:33 +0200)
 - Add platform independent clipboard layer for Nasal access
   to clipboard (thanks to Hooray for basic code)
 - Add Windows clipboard access
 - Add partial X11 clipboard access (only reading from clipboard)
 - Add fallback clipboard for application internal clipboard if
   platform not supported
 - Add some helper functions to FGNasalSys

src/Scripting/CMakeLists.txt
src/Scripting/ClipboardFallback.cxx [new file with mode: 0644]
src/Scripting/ClipboardWindows.cxx [new file with mode: 0644]
src/Scripting/ClipboardX11.cxx [new file with mode: 0644]
src/Scripting/NasalClipboard.cxx [new file with mode: 0644]
src/Scripting/NasalClipboard.hxx [new file with mode: 0644]
src/Scripting/NasalSys.cxx
src/Scripting/NasalSys.hxx

index 44c9563b243a68b566b40caa4debc00d425210f0..4a528c91f0c57beece91840e9ff70cc739ba94f8 100644 (file)
@@ -5,14 +5,26 @@ set(SOURCES
        nasal-props.cxx
     NasalPositioned.cxx
     NasalCanvas.cxx
+    NasalClipboard.cxx
        )
 
 set(HEADERS
        NasalSys.hxx
     NasalPositioned.hxx
     NasalCanvas.hxx
+    NasalClipboard.hxx
        )
 
-       
+if(WIN32)
+  list(APPEND SOURCES ClipboardWindows.cxx)
+else()
+  find_package(X11)
+  if(X11_FOUND)
+    list(APPEND SOURCES ClipboardX11.cxx)
+  else()
+    list(APPEND SOURCES ClipboardFallback.cxx)
+  endif()
+endif()
+
 flightgear_component(Scripting "${SOURCES}" "${HEADERS}")
 
diff --git a/src/Scripting/ClipboardFallback.cxx b/src/Scripting/ClipboardFallback.cxx
new file mode 100644 (file)
index 0000000..840be24
--- /dev/null
@@ -0,0 +1,64 @@
+// Fallback implementation of clipboard access for Nasal. Copy and edit for
+// implementing support of other platforms
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#include "NasalClipboard.hxx"
+
+#include <simgear/debug/logstream.hxx>
+
+
+/**
+ * Provide a basic clipboard whose contents are only available to FlightGear
+ * itself
+ */
+class ClipboardFallback:
+  public NasalClipboard
+{
+  public:
+
+    /**
+     * Get clipboard contents as text
+     */
+    virtual std::string getText(Type type)
+    {
+      return type == CLIPBOARD ? _clipboard : _selection;
+    }
+
+    /**
+     * Set clipboard contents as text
+     */
+    virtual bool setText(const std::string& text, Type type)
+    {
+      if( type == CLIPBOARD )
+        _clipboard = text;
+      else
+        _selection = text;
+      return true;
+    }
+
+  protected:
+
+    std::string _clipboard,
+                _selection;
+};
+
+//------------------------------------------------------------------------------
+NasalClipboard::Ptr NasalClipboard::create()
+{
+  return NasalClipboard::Ptr(new ClipboardFallback);
+}
diff --git a/src/Scripting/ClipboardWindows.cxx b/src/Scripting/ClipboardWindows.cxx
new file mode 100644 (file)
index 0000000..1ae606d
--- /dev/null
@@ -0,0 +1,108 @@
+// Windows implementation of clipboard access for Nasal
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#include "NasalClipboard.hxx"
+
+#include <simgear/debug/logstream.hxx>
+#include <windows.h>
+
+/**
+ * Windows does only support on clipboard and no selection. We fake also the X11
+ * selection buffer - at least inside FlightGear
+ */
+class ClipboardWindows:
+  public NasalClipboard
+{
+  public:
+
+    /**
+     * Get clipboard contents as text
+     */
+    virtual std::string getText(Type type)
+    {
+      if( type == CLIPBOARD )
+      {
+        std::string data;
+
+        if( !OpenClipboard(NULL) )
+          return data;
+
+        HANDLE hData = GetClipboardData( CF_TEXT );
+        char* buff = (char*)GlobalLock( hData );
+        if (buff)
+          data = buff;
+        GlobalUnlock( hData );
+        CloseClipboard();
+
+        return data;
+      }
+      else
+        return _selection;
+    }
+
+    /**
+     * Set clipboard contents as text
+     */
+    virtual bool setText(const std::string& text, Type type)
+    {
+      if( type == CLIPBOARD )
+      {
+        if( !OpenClipboard(NULL) )
+          return false;
+
+        bool ret = true;
+        if( !EmptyClipboard() )
+          ret = false;
+        else if( !text.empty() )
+        {
+          HGLOBAL hGlob = GlobalAlloc(GMEM_MOVEABLE, text.size() + 1);
+          if( !hGlob )
+            ret = false;
+          else
+          {
+            memcpy(GlobalLock(hGlob), (char*)&text[0], text.size() + 1);
+            GlobalUnlock(hGlob);
+
+            if( !SetClipboardData(CF_TEXT, hGlob) )
+            {
+              GlobalFree(hGlob);
+              ret = false;
+            }
+          }
+        }
+
+        CloseClipboard();
+        return ret;
+      }
+      else
+      {
+        _selection = text;
+        return true;
+      }
+    }
+
+  protected:
+
+    std::string _selection;
+};
+
+//------------------------------------------------------------------------------
+NasalClipboard::Ptr NasalClipboard::create()
+{
+  return NasalClipboard::Ptr(new ClipboardWindows);
+}
diff --git a/src/Scripting/ClipboardX11.cxx b/src/Scripting/ClipboardX11.cxx
new file mode 100644 (file)
index 0000000..6108c52
--- /dev/null
@@ -0,0 +1,218 @@
+// X11 implementation of clipboard access for Nasal
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#include "NasalClipboard.hxx"
+
+#include <simgear/debug/logstream.hxx>
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+class ClipboardX11:
+  public NasalClipboard
+{
+  public:
+    ClipboardX11():
+      _display( XOpenDisplay(NULL) ),
+      _window( XCreateSimpleWindow(
+        _display,
+        DefaultRootWindow(_display),
+        0, 0, 1, 1, // dummy dimensions -> window will never be mapped
+        0,
+        0, 0
+      ) ),
+      _atom_targets( XInternAtom(_display, "TARGETS", 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);
+    }
+
+    /**
+     * Get clipboard contents as text
+     */
+    virtual std::string getText(Type type)
+    {
+      Atom atom_type = (type == CLIPBOARD ? _atom_clipboard : _atom_primary);
+
+      //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;
+
+      for(int cnt = 0; cnt < 5;)
+      {
+        XEvent event;
+        XNextEvent(_display, &event);
+
+        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;
+
+            SG_LOG
+            (
+              SG_NASAL,
+              SG_WARN,
+              "ClipboardX11::getText: Conversion failed: "
+                                     "target=" << getAtomName(target)
+            );
+            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;
+            }
+          }
+        }
+        else
+        {
+          SG_LOG
+          (
+            SG_NASAL,
+            SG_WARN,
+            "ClipboardX11::getText: unexpected XEvent: " << event.type
+          );
+          break;
+        }
+      }
+
+      return std::string();
+    }
+
+    /**
+     * Set clipboard contents as text
+     */
+    virtual bool setText(const std::string& text, Type type)
+    {
+      SG_LOG
+      (
+        SG_NASAL,
+        SG_ALERT,
+        "ClipboardX11::setText: not yet implemented!"
+      );
+      return false;
+    }
+
+  protected:
+
+    Display    *_display;
+    Window      _window;
+    Atom        _atom_targets,
+                _atom_primary,
+                _atom_clipboard;
+
+    struct Property
+    {
+      unsigned char *data;
+      int format, num_items;
+      Atom type;
+    };
+
+    // Get all data from a property
+    Property readProperty(Window w, Atom property)
+    {
+      Atom actual_type;
+      int actual_format;
+      unsigned long nitems;
+      unsigned long bytes_after;
+      unsigned char *ret=0;
+
+      int read_bytes = 1024;
+
+      //Keep trying to read the property until there are no
+      //bytes unread.
+      do
+      {
+        if( ret )
+          XFree(ret);
+
+        XGetWindowProperty
+        (
+          _display, w, property, 0, read_bytes, False, AnyPropertyType,
+          &actual_type, &actual_format, &nitems, &bytes_after,
+          &ret
+        );
+
+        read_bytes *= 2;
+      } while( bytes_after );
+
+      Property p = {ret, actual_format, nitems, actual_type};
+      return p;
+    }
+
+    std::string getAtomName(Atom atom)
+    {
+      return atom == None ? "None" : XGetAtomName(_display, atom);
+    }
+
+};
+
+//------------------------------------------------------------------------------
+NasalClipboard::Ptr NasalClipboard::create()
+{
+  return NasalClipboard::Ptr(new ClipboardX11);
+}
diff --git a/src/Scripting/NasalClipboard.cxx b/src/Scripting/NasalClipboard.cxx
new file mode 100644 (file)
index 0000000..db148dc
--- /dev/null
@@ -0,0 +1,131 @@
+// X11 implementation of clipboard access for Nasal
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "NasalClipboard.hxx"
+#include "NasalSys.hxx"
+
+#include <boost/algorithm/string/case_conv.hpp>
+#include <cstddef>
+
+/*
+ *  Nasal wrappers for setting/getting clipboard text
+ */
+//------------------------------------------------------------------------------
+static NasalClipboard::Type parseType(naContext c, int argc, naRef* args, int i)
+{
+  if( argc > i )
+  {
+    if( !naIsString(args[i]) )
+      naRuntimeError(c, "clipboard: invalid arg (not a string)");
+
+    std::string type_str( naStr_data(args[i]) );
+    boost::to_upper(type_str);
+
+    if( type_str == "CLIPBOARD" )
+      return NasalClipboard::CLIPBOARD;
+    else if( type_str == "PRIMARY" || type_str == "SELECTION" )
+      return NasalClipboard::PRIMARY;
+    else
+      naRuntimeError(c, "clipboard: unknown clipboard type");
+  }
+
+  return NasalClipboard::CLIPBOARD;
+}
+
+//------------------------------------------------------------------------------
+static naRef f_setClipboardText(naContext c, naRef me, int argc, naRef* args)
+{
+  if( argc < 1 || argc > 2 )
+    naRuntimeError( c, "clipboard.setText() expects 1 or 2 arguments: "
+                       "text, [, type = \"CLIPBOARD\"]" );
+
+  if( !naIsString(args[0]) )
+    naRuntimeError(c, "clipboard.setText() invalid arg (arg 0 not a string)");
+
+  return
+    naNum
+    (
+      NasalClipboard::getInstance()->setText( naStr_data(args[0]),
+                                              parseType(c, argc, args, 1) )
+    );
+}
+
+//------------------------------------------------------------------------------
+static naRef f_getClipboardText(naContext c, naRef me, int argc, naRef* args)
+{
+  if( argc > 1 )
+    naRuntimeError(c, "clipboard.getText() accepts max 1 arg: "
+                      "[type = \"CLIPBOARD\"]" );
+
+  const std::string& text =
+    NasalClipboard::getInstance()->getText(parseType(c, argc, args, 0));
+
+  // TODO create some nasal helper functions (eg. stringToNasal)
+  //      some functions are available spread over different files (eg.
+  //      NasalPositioned.cxx)
+  return naStr_fromdata(naNewString(c), text.c_str(), text.length());
+}
+
+//------------------------------------------------------------------------------
+// Table of extension functions, terminate with 0,0
+static struct {const char* name; naCFunction func; } funcs[] = {
+  { "setText", f_setClipboardText },
+  { "getText", f_getClipboardText },
+  { 0,0 } // TERMINATION
+};
+
+//------------------------------------------------------------------------------
+NasalClipboard::Ptr NasalClipboard::_clipboard;
+naRef NasalClipboard::_clipboard_hash;
+
+//------------------------------------------------------------------------------
+NasalClipboard::~NasalClipboard()
+{
+
+}
+
+//------------------------------------------------------------------------------
+void NasalClipboard::init(FGNasalSys *nasal)
+{
+  _clipboard = create();
+  _clipboard_hash = naNewHash(nasal->context());
+
+  nasal->globalsSet("clipboard", _clipboard_hash);
+
+  for(size_t i=0;funcs[i].name;i++)
+  {
+    nasal->hashset
+    (
+      _clipboard_hash,
+      funcs[i].name,
+      naNewFunc(nasal->context(), naNewCCode(nasal->context(), funcs[i].func))
+    );
+
+    SG_LOG(SG_NASAL, SG_DEBUG, "Adding clipboard function: " << funcs[i].name );
+  }
+}
+
+//------------------------------------------------------------------------------
+NasalClipboard::Ptr NasalClipboard::getInstance()
+{
+  return _clipboard;
+}
diff --git a/src/Scripting/NasalClipboard.hxx b/src/Scripting/NasalClipboard.hxx
new file mode 100644 (file)
index 0000000..fca3979
--- /dev/null
@@ -0,0 +1,72 @@
+// Clipboard access for Nasal
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef NASAL_CLIPOARD_HXX_
+#define NASAL_CLIPOARD_HXX_
+
+#include <simgear/nasal/nasal.h>
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+class FGNasalSys;
+class NasalClipboard
+{
+  public:
+
+    enum Type
+    {
+      /// Standard clipboard as supported by nearly all operating systems
+      CLIPBOARD,
+
+      /// X11 platforms support also a mode called PRIMARY selection which
+      /// contains the current (mouse) selection and can typically be inserted
+      /// via a press on the middle mouse button
+      PRIMARY
+    };
+
+    typedef boost::shared_ptr<NasalClipboard> Ptr;
+
+    virtual std::string getText(Type type = CLIPBOARD) = 0;
+    virtual bool setText( const std::string& text,
+                          Type type = CLIPBOARD ) = 0;
+
+    /**
+     * Sets up the clipboard and puts all the extension functions into a new
+     * "clipboard" namespace.
+     */
+    static void init(FGNasalSys *nasal);
+
+    /**
+     * Get clipboard platform specific instance
+     */
+    static Ptr getInstance();
+
+  protected:
+
+    static Ptr      _clipboard;
+    static naRef    _clipboard_hash;
+
+    /**
+     * Implementation supplied by actual platform implementation
+     */
+    static Ptr create();
+
+    virtual ~NasalClipboard() = 0;
+};
+
+#endif /* NASAL_CLIPOARD_HXX_ */
index a1de1e79452588230bf1838f29cd1f0e68533055..5dd28ed1ba6a169f17adb83d7171a3b0259c0e7f 100644 (file)
 #include "NasalSys.hxx"
 #include "NasalPositioned.hxx"
 #include "NasalCanvas.hxx"
+#include "NasalClipboard.hxx"
 
 #include <Main/globals.hxx>
 #include <Main/util.hxx>
 #include <Main/fg_props.hxx>
 
+
 using std::map;
 
 static FGNasalSys* nasalSys = 0;
@@ -101,6 +103,20 @@ FGNasalSys::FGNasalSys()
     _callCount = 0;
 }
 
+// Utility.  Sets a named key in a hash by C string, rather than nasal
+// string object.
+void FGNasalSys::hashset(naRef hash, const char* key, naRef val)
+{
+    naRef s = naNewString(_context);
+    naStr_fromdata(s, (char*)key, strlen(key));
+    naHash_set(hash, s, val);
+}
+
+void FGNasalSys::globalsSet(const char* key, naRef val)
+{
+  hashset(_globals, key, val);
+}
+
 naRef FGNasalSys::call(naRef code, int argc, naRef* args, naRef locals)
 {
   return callMethod(code, naNil(), argc, args, locals);
@@ -170,15 +186,6 @@ FGNasalScript* FGNasalSys::parseScript(const char* src, const char* name)
     return script;
 }
 
-// Utility.  Sets a named key in a hash by C string, rather than nasal
-// string object.
-void FGNasalSys::hashset(naRef hash, const char* key, naRef val)
-{
-    naRef s = naNewString(_context);
-    naStr_fromdata(s, (char*)key, strlen(key));
-    naHash_set(hash, s, val);
-}
-
 // The get/setprop functions accept a *list* of strings and walk
 // through the property tree with them to find the appropriate node.
 // This allows a Nasal object to hold onto a property path and use it
@@ -551,7 +558,8 @@ void FGNasalSys::init()
 
     initNasalPositioned(_globals, _context, _gcHash);
     initNasalCanvas(_globals, _context, _gcHash);
-  
+    NasalClipboard::init(this);
+
     // Now load the various source files in the Nasal directory
     simgear::Dir nasalDir(SGPath(globals->get_fg_root(), "Nasal"));
     loadScriptDirectory(nasalDir);
index 0dad5fd530f1d7d413d4be6187c50cb257235609..18ef05314c115682d63b68d3daefbe437c10809a 100644 (file)
@@ -114,6 +114,16 @@ public:
 
     void deleteModule(const char* moduleName);
 
+    /**
+     * Set member of specified hash to given value
+     */
+    void hashset(naRef hash, const char* key, naRef val);
+
+    /**
+     * Set member of globals hash to given value
+     */
+    void globalsSet(const char* key, naRef val);
+
     naRef call(naRef code, int argc, naRef* args, naRef locals);
   
     naRef callMethod(naRef code, naRef self, int argc, naRef* args, naRef locals);
@@ -168,7 +178,6 @@ private:
     void loadPropertyScripts(SGPropertyNode* n);
     void loadScriptDirectory(simgear::Dir nasalDir);
     void addModule(string moduleName, simgear::PathList scripts);
-    void hashset(naRef hash, const char* key, naRef val);
     void logError(naContext);
     naRef parse(const char* filename, const char* buf, int len);
     naRef genPropsModule();