diff --git a/code/nel/include/nel/3d/driver_user.h b/code/nel/include/nel/3d/driver_user.h index 699691d89..1d079a2b7 100644 --- a/code/nel/include/nel/3d/driver_user.h +++ b/code/nel/include/nel/3d/driver_user.h @@ -535,6 +535,11 @@ public: virtual void deleteWaterEnvMap(UWaterEnvMap *map); // @} + // Copy a string to system clipboard. + virtual bool copyTextToClipboard(const ucstring &text); + + // Paste a string from system clipboard. + virtual bool pasteTextFromClipboard(ucstring &text); virtual uint64 getSwapBufferCounter(); diff --git a/code/nel/include/nel/3d/u_driver.h b/code/nel/include/nel/3d/u_driver.h index ce421b46d..9fe5c86d0 100644 --- a/code/nel/include/nel/3d/u_driver.h +++ b/code/nel/include/nel/3d/u_driver.h @@ -816,6 +816,15 @@ public: virtual uint64 getSwapBufferCounter() = 0; + /// \name Clipboard management + // @{ + // Copy a string to system clipboard. + virtual bool copyTextToClipboard(const ucstring &text) =0; + + // Paste a string from system clipboard. + virtual bool pasteTextFromClipboard(ucstring &text) =0; + // @} + public: /** diff --git a/code/nel/include/nel/misc/event_emitter_multi.h b/code/nel/include/nel/misc/event_emitter_multi.h index 6abdf08d8..73e28c16e 100644 --- a/code/nel/include/nel/misc/event_emitter_multi.h +++ b/code/nel/include/nel/misc/event_emitter_multi.h @@ -48,6 +48,10 @@ public: /// From IEventEmitter. This call submitEvents on all the emitters virtual void submitEvents(CEventServer &server, bool allWindows); virtual void emulateMouseRawMode(bool enable); + + virtual bool copyTextToClipboard(const ucstring &text); + virtual bool pasteTextFromClipboard(ucstring &text); + private: typedef std::vector > TEmitterCont; TEmitterCont _Emitters; diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp index d6af8cabc..0890a9084 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp @@ -3831,5 +3831,13 @@ void CDriverD3D::findNearestFullscreenVideoMode() } } } +bool CDriverD3D::copyTextToClipboard(const ucstring &text) +{ + return _EventEmitter.copyTextToClipboard(text); +} +bool CDriverD3D::pasteTextFromClipboard(ucstring &text) +{ + return _EventEmitter.pasteTextFromClipboard(text); +} } // NL3D diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.h b/code/nel/src/3d/driver/direct3d/driver_direct3d.h index ccd414782..82f73befc 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.h +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.h @@ -2392,6 +2392,10 @@ public: void deleteIndexBuffer(CIBDrvInfosD3D *ib); // Build 16 bit index buffer for quad bool buildQuadIndexBuffer(); + + virtual bool copyTextToClipboard(const ucstring &text); + virtual bool pasteTextFromClipboard(ucstring &text); + public: #ifdef NL_DEBUG std::set _LockedBuffers; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl.h b/code/nel/src/3d/driver/opengl/driver_opengl.h index cf4f93a22..8e6b3a9b1 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl.h +++ b/code/nel/src/3d/driver/opengl/driver_opengl.h @@ -329,6 +329,9 @@ public: return _win; } + virtual bool copyTextToClipboard(const ucstring &text); + virtual bool pasteTextFromClipboard(ucstring &text); + virtual uint32 getAvailableVertexAGPMemory (); virtual uint32 getAvailableVertexVRAMMemory (); diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp index f82fbe746..7656e4242 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp @@ -2932,4 +2932,14 @@ void CDriverGL::setupApplicationMenu() } #endif +bool CDriverGL::copyTextToClipboard(const ucstring &text) +{ + return _EventEmitter.copyTextToClipboard(text); +} + +bool CDriverGL::pasteTextFromClipboard(ucstring &text) +{ + return _EventEmitter.pasteTextFromClipboard(text); +} + } // NL3D diff --git a/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.cpp b/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.cpp index 86014e6cb..e1ce85dea 100644 --- a/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.cpp +++ b/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.cpp @@ -154,6 +154,16 @@ static NLMISC::TKey virtualKeycodeToNelKey(unsigned short keycode) return NLMISC::KeyNOKEY; } +bool CCocoaEventEmitter::pasteTextFromClipboard(ucstring &text) +{ + return false; +} + +bool CCocoaEventEmitter::copyTextToClipboard(const ucstring &text) +{ + return false; +} + /// convert modifier key state to nel internal modifier key state static NLMISC::TKeyButton modifierFlagsToNelKeyButton(unsigned int modifierFlags) { diff --git a/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.h b/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.h index 72bfbb718..6680552b7 100644 --- a/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.h +++ b/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.h @@ -31,8 +31,9 @@ public: virtual void submitEvents(CEventServer & server, bool allWindows); virtual void emulateMouseRawMode(bool enable); -}; - + + virtual bool copyTextToClipboard(const ucstring &text); + virtual bool pasteTextFromClipboard(ucstring &text);}; } #endif diff --git a/code/nel/src/3d/driver/opengl/unix_event_emitter.cpp b/code/nel/src/3d/driver/opengl/unix_event_emitter.cpp index f5de38371..6ae4c91c3 100644 --- a/code/nel/src/3d/driver/opengl/unix_event_emitter.cpp +++ b/code/nel/src/3d/driver/opengl/unix_event_emitter.cpp @@ -19,6 +19,8 @@ #if defined(NL_OS_UNIX) && !defined(NL_OS_MAC) +#include +#include #include #include #include @@ -33,6 +35,7 @@ CUnixEventEmitter::CUnixEventEmitter ():_dpy(NULL), _win(0), _emulateRawMode(fal { _im = 0; _ic = 0; + _SelectionOwned=false; } CUnixEventEmitter::~CUnixEventEmitter() @@ -47,12 +50,24 @@ void CUnixEventEmitter::init(Display *dpy, Window win, NL3D::IDriver *driver) _win = win; _driver = driver; - XSelectInput (_dpy, _win, KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask|StructureNotifyMask); + XSelectInput (_dpy, _win, KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask|StructureNotifyMask|ExposureMask); + _PrecomputedAtom[0] = XInternAtom(dpy, "CLIPBOARD", False); + #define XA_CLIPBOARD _PrecomputedAtom[0] + _PrecomputedAtom[1] = XInternAtom(dpy, "UTF8_STRING", False); + #define XA_UTF8_STRING _PrecomputedAtom[1] + _PrecomputedAtom[2] = XInternAtom(dpy, "TARGETS", False); + #define XA_TARGETS _PrecomputedAtom[2] + _PrecomputedAtom[3] = XInternAtom(dpy, "ATOM", False); + //#define XA_ATOM _PrecomputedAtom[3] + _PrecomputedAtom[4] = XInternAtom(dpy, "NeL_SEL", False); + #define XA_NEL_SEL _PrecomputedAtom[4] + _PrecomputedAtom[5] = XInternAtom(dpy, "TEXT", False); + #define XA_TEXT _PrecomputedAtom[5] /* TODO: implements all useful events processing EnterWindowMask|LeaveWindowMask|ButtonMotionMask|Button1MotionMask|Button2MotionMask| - Button3MotionMask|Button4MotionMask|Button5MotionMask|KeymapStateMask|ExposureMask| + Button3MotionMask|Button4MotionMask|Button5MotionMask|KeymapStateMask| SubstructureNotifyMask|VisibilityChangeMask|FocusChangeMask|PropertyChangeMask| ColormapChangeMask|OwnerGrabButtonMask */ @@ -578,6 +593,67 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server) } break; } + case SelectionRequest: + { + XEvent respond; + XSelectionRequestEvent req = event.xselectionrequest; + + respond.xselection.type= SelectionNotify; + respond.xselection.display= req.display; + respond.xselection.requestor= req.requestor; + respond.xselection.selection=req.selection; + respond.xselection.target= req.target; + respond.xselection.time = req.time; + respond.xselection.property = req.property; + + if (req.property == None) + { + respond.xselection.property = req.target; + } + if (req.target == XA_TARGETS) + { + nlwarning("Client is asking for TARGETS"); + + Atom targets[] = + { + XA_TARGETS, + XA_TEXT, + XA_UTF8_STRING + }; + + respond.xselection.property = req.property; + + XChangeProperty(req.display, req.requestor, req.property, XA_ATOM, 32, PropModeReplace, (unsigned char *)targets, 3 /* number of element */); + } + else if (req.target == XA_TEXT || req.target == XA_STRING ) + { + nlwarning("client want TEXT"); + respond.xselection.property = req.property; + std::string str = _CopiedString.toString(); + XChangeProperty(req.display, req.requestor, req.property, XA_STRING, 8, PropModeReplace, (const unsigned char*)str.c_str(), str.length()); + } + else if (req.target == XA_UTF8_STRING) + { + nlwarning("Client want UTF8 STRING"); + respond.xselection.property=req.property; + std::string str = _CopiedString.toUtf8(); + XChangeProperty(req.display, req.requestor, respond.xselection.property, XInternAtom(_dpy, "UTF8_STRING", False), 8, PropModeReplace, (const unsigned char*)str.c_str(), strlen((char*)str.c_str())); + } + else + { + nlwarning("Target doesn't want a string %u", (uint)req.target); // Note: Calling XGetAtomName with arbitrary value crash the client, maybe req.target have been sanitized by X11 server + respond.xselection.property = None; + } + + XSendEvent (_dpy, req.requestor, 0, 0, &respond); + + break; + } + case SelectionClear: + nlwarning("SelectionClear:"); + _SelectionOwned = false; + _CopiedString = ""; + break; case FocusIn: // keyboard focus if (_ic) XSetICFocus(_ic); @@ -605,6 +681,180 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server) return true; } +/** + * This function copy a selection into propertyName. + * It is subject to timeout. + * + * @param selection: A Selection Atom + * @param requestedFormat: Target format Atom + * @param propertyName: Target property Atom + * @return true if successfull, false if timeout occured or X11 call failed + */ +bool CUnixEventEmitter::prepareSelectionContent (Atom selection, Atom requestedFormat, Atom propertyName) +{ + XConvertSelection(_dpy, selection, requestedFormat, propertyName, _win, CurrentTime); + + XSync(_dpy, False); + + int i = 0; + + bool gotReply = false; + + do + { + XEvent event; + usleep(500); + gotReply = XCheckTypedWindowEvent(_dpy, _win, SelectionNotify, &event); + if (gotReply) + { + return true; + } + i++; + } + while (i<20); + + return false; +} + +bool CUnixEventEmitter::copyTextToClipboard(const ucstring &text) +{ + _CopiedString = text; + + XSetSelectionOwner (_dpy, XA_CLIPBOARD, _win, CurrentTime); + { + Window selectionOwner = XGetSelectionOwner (_dpy, XA_CLIPBOARD); + + if ( selectionOwner != _win ) + { + nlwarning("Can't aquire selection"); + return false; + } + + _SelectionOwned = true; + + nlwarning("Owning selection"); + + return true; + } + + nlwarning("Paste: Can't acquire selection."); + + return false; +} + +bool CUnixEventEmitter::pasteTextFromClipboard(ucstring &text) +{ + // check if we own the selection + if (_SelectionOwned) + { + text = _CopiedString; + return true; + } + + Window selectionOwner = XGetSelectionOwner (_dpy, XA_CLIPBOARD); + + if (selectionOwner != None) + { + Atom *supportedTargets; + uint8 *data; + sint result; + unsigned long nitems, bytesLeft; + Atom actualType; + sint actualFormat; + sint bestTargetElect=0, bestTarget=0; + + nlwarning("Selection owner is %u", (uint)selectionOwner); + + // Find supported methods + bool bres = prepareSelectionContent(XA_CLIPBOARD, XA_TARGETS, XA_NEL_SEL); + + if (!bres) + { + nlwarning("Paste: Cannot ennumerate TARGETS"); + return false; + } + + result = XGetWindowProperty(_dpy, _win, XA_NEL_SEL, 0, XMaxRequestSize(_dpy), False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesLeft,(unsigned char**) &supportedTargets); + + if (result != Success) + { + return false; + } + + if ( bytesLeft>0 ) + { + nlwarning("Paste: Supported TARGETS list too long."); // We hope we find what we need in the first packet. + } + + // Elect best type + for (uint i=0; i < nitems; i++) + { + nldebug(" - Type=%s", XGetAtomName(_dpy, supportedTargets[i])); + + if (supportedTargets[i] == XA_STRING ) + { + if (bestTargetElect < 1) + { + bestTarget = XA_STRING; + bestTargetElect = 1; + } + } + + if (supportedTargets[i] == XA_UTF8_STRING ) + { + if (bestTargetElect < 2) + { + bestTarget = XA_UTF8_STRING; + bestTargetElect = 2; + } + } + } + + XFree(supportedTargets); + + if (!bestTargetElect) + { + nlwarning("Paste buffer is not a text buffer."); + return false; + } + + // Ask for selection lenght && copy to buffer + bres = prepareSelectionContent(XA_CLIPBOARD, bestTarget, XA_NEL_SEL); + + if (!bres) + { + nlwarning ("Paste: cannot obtain data. Aborting."); + return false; + } + + XGetWindowProperty(_dpy, _win, XA_NEL_SEL, 0, XMaxRequestSize(_dpy), False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesLeft,(unsigned char**) &data); + + std::string tmpData = (const char*)data; + XFree(data); + + switch (bestTargetElect) + { + case 1: // XA_STRING + text = tmpData; + return true; + + case 2: // XA_UTF8_STRING + text = ucstring::makeFromUtf8(tmpData); + return true; + + default: + break; + } + + nlwarning("Paste: buffer is not a text buffer."); + } + + nlwarning("Paste: error !"); + + return false; +} + + } // NLMISC #endif // defined(NL_OS_UNIX) && !defined(NL_OS_MAC) diff --git a/code/nel/src/3d/driver/opengl/unix_event_emitter.h b/code/nel/src/3d/driver/opengl/unix_event_emitter.h index c6232330a..48d1f7803 100644 --- a/code/nel/src/3d/driver/opengl/unix_event_emitter.h +++ b/code/nel/src/3d/driver/opengl/unix_event_emitter.h @@ -65,6 +65,16 @@ public: */ bool processMessage(XEvent &event, CEventServer *server = NULL); + /** + * Copy a string to system clipboard. + */ + virtual bool copyTextToClipboard(const ucstring &text); + + /* + * Paste a string from system clipboard. + */ + virtual bool pasteTextFromClipboard(ucstring &text); + private: // Private internal server message @@ -88,6 +98,7 @@ private: }; void createIM(); + bool prepareSelectionContent (Atom selection, Atom requestedFormat, Atom propertyName); Display* _dpy; Window _win; @@ -97,6 +108,9 @@ private: bool _emulateRawMode; NL3D::IDriver* _driver; CUnixEventServer _InternalServer; + ucstring _CopiedString; + Atom _PrecomputedAtom[6]; + bool _SelectionOwned; }; diff --git a/code/nel/src/3d/driver_user.cpp b/code/nel/src/3d/driver_user.cpp index 523aaf4ba..d6fccb362 100644 --- a/code/nel/src/3d/driver_user.cpp +++ b/code/nel/src/3d/driver_user.cpp @@ -1938,5 +1938,14 @@ bool CDriverUser::setRenderTarget(class UTexture & uTex, uint32 x, uint32 y, uin return result; } +bool CDriverUser::copyTextToClipboard(const ucstring &text) +{ + return _Driver->copyTextToClipboard(text); +} + +bool CDriverUser::pasteTextFromClipboard(ucstring &text) +{ + return _Driver->pasteTextFromClipboard(text); +} } // NL3D diff --git a/code/nel/src/misc/event_emitter_multi.cpp b/code/nel/src/misc/event_emitter_multi.cpp index 1c0e19084..815abd675 100644 --- a/code/nel/src/misc/event_emitter_multi.cpp +++ b/code/nel/src/misc/event_emitter_multi.cpp @@ -16,7 +16,7 @@ #include "stdmisc.h" #include "nel/misc/event_emitter_multi.h" - +#include "nel/misc/system_utils.h" namespace NLMISC { @@ -99,7 +99,17 @@ const IEventEmitter *CEventEmitterMulti::getEmitter(uint index) const return _Emitters[index].first; } +bool CEventEmitterMulti::copyTextToClipboard(const ucstring &text) +{ + // Naush: wrapped to old API to avoid duplicate code + return CSystemUtils::copyTextToClipboard(text); +} +bool CEventEmitterMulti::pasteTextFromClipboard(ucstring &text) +{ + // Naush: wrapped to old API to avoid duplicate code + return CSystemUtils::pasteTextFromClipboard(text); +} } // NLMISC