X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FScripting%2FClipboardX11.cxx;h=c8747863b74521e90cac3601da2a0ea95d82ad38;hb=9c63b7713307eb4db9f6df7959fe83977f561c42;hp=6108c52425ab260384561adade4bb14643625038;hpb=aa9d0e3a8a2ef8570f18f463b1dac6afe8777953;p=flightgear.git diff --git a/src/Scripting/ClipboardX11.cxx b/src/Scripting/ClipboardX11.cxx index 6108c5242..c8747863b 100644 --- a/src/Scripting/ClipboardX11.cxx +++ b/src/Scripting/ClipboardX11.cxx @@ -16,6 +16,14 @@ // 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 @@ -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(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; + } + }; //------------------------------------------------------------------------------