From d56d0ebc1a7b70a158462014e40b4a0ad702742b Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 17 Oct 2010 16:29:18 +0200 Subject: [PATCH] Changed: #1038 Implement copy/paste for Linux --- code/nel/include/nel/misc/events.h | 16 + .../3d/driver/opengl/unix_event_emitter.cpp | 322 ++++++++---------- .../src/3d/driver/opengl/unix_event_emitter.h | 2 - 3 files changed, 164 insertions(+), 176 deletions(-) diff --git a/code/nel/include/nel/misc/events.h b/code/nel/include/nel/misc/events.h index 23dd83d5f..53418c8be 100644 --- a/code/nel/include/nel/misc/events.h +++ b/code/nel/include/nel/misc/events.h @@ -19,6 +19,7 @@ #include "types_nl.h" #include "class_id.h" +#include "ucstring.h" #include #include @@ -58,6 +59,7 @@ protected: const CClassId EventKeyDownId (0x3c2643da, 0x43f802a1); const CClassId EventKeyUpId (0x1e62e85, 0x68a35d46); const CClassId EventCharId (0x552255fe, 0x75a2373f); +const CClassId EventStringId (0x49b5af8f, 0x7f52cd26); // Window events const CClassId EventActivateId (0x7da66b0a, 0x1ef74519); @@ -327,6 +329,20 @@ private: }; +/** + * CEventString + */ +class CEventString : public CEventKey +{ +public: + CEventString (const ucstring &str, IEventEmitter* emitter) : CEventKey (noKeyButton, emitter, EventStringId) + { + String = str; + } + ucstring String; + + virtual CEvent *clone() const {return new CEventString(*this);} +}; /** * CEventMouse. 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 6ae4c91c3..9f86c8c89 100644 --- a/code/nel/src/3d/driver/opengl/unix_event_emitter.cpp +++ b/code/nel/src/3d/driver/opengl/unix_event_emitter.cpp @@ -29,6 +29,11 @@ typedef bool (*x11Proc)(NL3D::IDriver *drv, XEvent *e); +static Atom XA_CLIPBOARD = 0; +static Atom XA_UTF8_STRING = 0; +static Atom XA_TARGETS = 0; +static Atom XA_NEL_SEL = 0; + namespace NLMISC { CUnixEventEmitter::CUnixEventEmitter ():_dpy(NULL), _win(0), _emulateRawMode(false), _driver(NULL) @@ -51,18 +56,12 @@ void CUnixEventEmitter::init(Display *dpy, Window win, NL3D::IDriver *driver) _driver = driver; 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] + + // define Atoms used by clipboard + XA_CLIPBOARD = XInternAtom(dpy, "CLIPBOARD", False); + XA_UTF8_STRING = XInternAtom(dpy, "UTF8_STRING", False); + XA_TARGETS = XInternAtom(dpy, "TARGETS", False); + XA_NEL_SEL = XInternAtom(dpy, "NeL_SEL", False); /* TODO: implements all useful events processing @@ -568,10 +567,23 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server) #ifdef X_HAVE_UTF8_STRING ucstring ucstr; ucstr.fromUtf8(Text); - server->postEvent (new CEventChar (ucstr[0], noKeyButton, this)); + + CEventChar *charEvent = new CEventChar (ucstr[0], noKeyButton, this); + + // raw if not processed by IME + charEvent->setRaw(keyCode != 0); + + server->postEvent (charEvent); #else for (int i = 0; i < c; i++) - server->postEvent (new CEventChar ((ucchar)(unsigned char)Text[i], noKeyButton, this)); + { + CEventChar *charEvent = new CEventChar ((ucchar)(unsigned char)Text[i], noKeyButton, this); + + // raw if not processed by IME + charEvent->setRaw(keyCode != 0); + + server->postEvent (charEvent); + } #endif } break; @@ -612,12 +624,10 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server) } if (req.target == XA_TARGETS) { - nlwarning("Client is asking for TARGETS"); - Atom targets[] = { XA_TARGETS, - XA_TEXT, + XA_STRING, XA_UTF8_STRING }; @@ -625,23 +635,21 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server) 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 ) + else if (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; + 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())); + XChangeProperty(req.display, req.requestor, respond.xselection.property, XA_UTF8_STRING, 8, PropModeReplace, (const unsigned char*)str.c_str(), str.length()); } 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 + // Note: Calling XGetAtomName with arbitrary value crash the client, maybe req.target have been sanitized by X11 server respond.xselection.property = None; } @@ -650,16 +658,111 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server) break; } case SelectionClear: - nlwarning("SelectionClear:"); _SelectionOwned = false; _CopiedString = ""; break; + case SelectionNotify: + { + Atom target = event.xselection.target; + + Atom actualType = 0; + int actualFormat = 0; + unsigned long nitems = 0, bytesLeft = 0; + + // some applications are sending ATOM and other TARGETS + if (target == XA_TARGETS || target == XA_ATOM) + { + Atom *supportedTargets = NULL; + + // list NeL selection properties + if (XGetWindowProperty(_dpy, _win, XA_NEL_SEL, 0, XMaxRequestSize(_dpy), False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesLeft, (unsigned char**)&supportedTargets) != Success) + return false; + + if (bytesLeft > 0) + { + nlwarning("Paste: Supported TARGETS list too long."); + } + + Atom bestTarget = 0; + sint bestTargetElect = 0; + + // Elect best type + for (uint i=0; i < nitems; i++) + { + // nlwarning(" - Type=%s (%u)", XGetAtomName(_dpy, supportedTargets[i]), (uint)supportedTargets[i]); + if (supportedTargets[i] == XA_UTF8_STRING ) + { + if (bestTargetElect < 2) + { + bestTarget = XA_UTF8_STRING; + bestTargetElect = 2; + } + } + else if (supportedTargets[i] == XA_STRING ) + { + if (bestTargetElect < 1) + { + bestTarget = XA_STRING; + bestTargetElect = 1; + } + } + } + + XFree(supportedTargets); + + if (!bestTargetElect) + { + nlwarning("Paste buffer is not a text buffer."); + return false; + } + + // request string conversion + XConvertSelection(_dpy, XA_CLIPBOARD, bestTarget, XA_NEL_SEL, _win, CurrentTime); + } + else if (target == XA_UTF8_STRING || target == XA_STRING) + { + uint8 *data = NULL; + + // get selection + if (XGetWindowProperty(_dpy, _win, XA_NEL_SEL, 0, XMaxRequestSize(_dpy), False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesLeft, (unsigned char**)&data) != Success) + return false; + + ucstring text; + std::string tmpData = (const char*)data; + XFree(data); + + // convert buffer to ucstring + if (target == XA_UTF8_STRING) + { + text = ucstring::makeFromUtf8(tmpData); + } + else if (target == XA_STRING) + { + text = tmpData; + } + else + { + nlwarning("Unknow format %u", (uint)target); + } + + // sent string event to event server + server->postEvent (new CEventString (text, this)); + } + else + { + nlwarning("Unknow target %u", (uint)target); + } + + break; + } case FocusIn: // keyboard focus + server->postEvent (new CEventSetFocus (true, this)); if (_ic) XSetICFocus(_ic); break; case FocusOut: // keyboard focus + server->postEvent (new CEventSetFocus (false, this)); if (_ic) XUnsetICFocus(_ic); break; case KeymapNotify: @@ -672,6 +775,12 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server) // XIM server has crashed createIM(); break; + case ClientMessage: + if ((xevent.xclient.format == 32) && (xevent.xclient.data.l[0] == videodata->WM_DELETE_WINDOW)) + { + server->postEvent (new CEventDestroyWindow (this)); + } + break; default: // nlinfo("UnknownEvent"); // XtDispatchEvent(&event); @@ -681,65 +790,23 @@ 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); + // NeL window is the owner of clipboard + XSetSelectionOwner(_dpy, XA_CLIPBOARD, _win, CurrentTime); + + // check we are owning the clipboard + if (XGetSelectionOwner(_dpy, XA_CLIPBOARD) != _win) { - Window selectionOwner = XGetSelectionOwner (_dpy, XA_CLIPBOARD); - - if ( selectionOwner != _win ) - { - nlwarning("Can't aquire selection"); - return false; - } - - _SelectionOwned = true; - - nlwarning("Owning selection"); - - return true; + nlwarning("Can't aquire selection"); + return false; } - nlwarning("Paste: Can't acquire selection."); + _SelectionOwned = true; - return false; + return true; } bool CUnixEventEmitter::pasteTextFromClipboard(ucstring &text) @@ -751,110 +818,17 @@ bool CUnixEventEmitter::pasteTextFromClipboard(ucstring &text) return true; } - Window selectionOwner = XGetSelectionOwner (_dpy, XA_CLIPBOARD); + // check if there is a data in clipboard + if (XGetSelectionOwner(_dpy, XA_CLIPBOARD) == None) + return false; - 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 !"); + // request supported methods + XConvertSelection(_dpy, XA_CLIPBOARD, XA_TARGETS, XA_NEL_SEL, _win, CurrentTime); + // don't return result now 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 48d1f7803..1a17ecd1f 100644 --- a/code/nel/src/3d/driver/opengl/unix_event_emitter.h +++ b/code/nel/src/3d/driver/opengl/unix_event_emitter.h @@ -98,7 +98,6 @@ private: }; void createIM(); - bool prepareSelectionContent (Atom selection, Atom requestedFormat, Atom propertyName); Display* _dpy; Window _win; @@ -109,7 +108,6 @@ private: NL3D::IDriver* _driver; CUnixEventServer _InternalServer; ucstring _CopiedString; - Atom _PrecomputedAtom[6]; bool _SelectionOwned; };