Changed: #1038 Implement copy/paste for Linux (patch provided by Naush)

This commit is contained in:
kervala 2010-10-16 22:16:09 +02:00
parent 4b860492be
commit 9bb9ef2151
13 changed files with 342 additions and 5 deletions

View file

@ -535,6 +535,11 @@ public:
virtual void deleteWaterEnvMap(UWaterEnvMap *map); 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(); virtual uint64 getSwapBufferCounter();

View file

@ -816,6 +816,15 @@ public:
virtual uint64 getSwapBufferCounter() = 0; 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: public:
/** /**

View file

@ -48,6 +48,10 @@ public:
/// From IEventEmitter. This call submitEvents on all the emitters /// From IEventEmitter. This call submitEvents on all the emitters
virtual void submitEvents(CEventServer &server, bool allWindows); virtual void submitEvents(CEventServer &server, bool allWindows);
virtual void emulateMouseRawMode(bool enable); virtual void emulateMouseRawMode(bool enable);
virtual bool copyTextToClipboard(const ucstring &text);
virtual bool pasteTextFromClipboard(ucstring &text);
private: private:
typedef std::vector<std::pair<IEventEmitter *, bool> > TEmitterCont; typedef std::vector<std::pair<IEventEmitter *, bool> > TEmitterCont;
TEmitterCont _Emitters; TEmitterCont _Emitters;

View file

@ -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 } // NL3D

View file

@ -2392,6 +2392,10 @@ public:
void deleteIndexBuffer(CIBDrvInfosD3D *ib); void deleteIndexBuffer(CIBDrvInfosD3D *ib);
// Build 16 bit index buffer for quad // Build 16 bit index buffer for quad
bool buildQuadIndexBuffer(); bool buildQuadIndexBuffer();
virtual bool copyTextToClipboard(const ucstring &text);
virtual bool pasteTextFromClipboard(ucstring &text);
public: public:
#ifdef NL_DEBUG #ifdef NL_DEBUG
std::set<CVBDrvInfosD3D *> _LockedBuffers; std::set<CVBDrvInfosD3D *> _LockedBuffers;

View file

@ -329,6 +329,9 @@ public:
return _win; return _win;
} }
virtual bool copyTextToClipboard(const ucstring &text);
virtual bool pasteTextFromClipboard(ucstring &text);
virtual uint32 getAvailableVertexAGPMemory (); virtual uint32 getAvailableVertexAGPMemory ();
virtual uint32 getAvailableVertexVRAMMemory (); virtual uint32 getAvailableVertexVRAMMemory ();

View file

@ -2932,4 +2932,14 @@ void CDriverGL::setupApplicationMenu()
} }
#endif #endif
bool CDriverGL::copyTextToClipboard(const ucstring &text)
{
return _EventEmitter.copyTextToClipboard(text);
}
bool CDriverGL::pasteTextFromClipboard(ucstring &text)
{
return _EventEmitter.pasteTextFromClipboard(text);
}
} // NL3D } // NL3D

View file

@ -154,6 +154,16 @@ static NLMISC::TKey virtualKeycodeToNelKey(unsigned short keycode)
return NLMISC::KeyNOKEY; 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 /// convert modifier key state to nel internal modifier key state
static NLMISC::TKeyButton modifierFlagsToNelKeyButton(unsigned int modifierFlags) static NLMISC::TKeyButton modifierFlagsToNelKeyButton(unsigned int modifierFlags)
{ {

View file

@ -31,8 +31,9 @@ public:
virtual void submitEvents(CEventServer & server, bool allWindows); virtual void submitEvents(CEventServer & server, bool allWindows);
virtual void emulateMouseRawMode(bool enable); virtual void emulateMouseRawMode(bool enable);
};
virtual bool copyTextToClipboard(const ucstring &text);
virtual bool pasteTextFromClipboard(ucstring &text);};
} }
#endif #endif

View file

@ -19,6 +19,8 @@
#if defined(NL_OS_UNIX) && !defined(NL_OS_MAC) #if defined(NL_OS_UNIX) && !defined(NL_OS_MAC)
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/keysym.h> #include <X11/keysym.h>
#include <GL/gl.h> #include <GL/gl.h>
#include <GL/glx.h> #include <GL/glx.h>
@ -33,6 +35,7 @@ CUnixEventEmitter::CUnixEventEmitter ():_dpy(NULL), _win(0), _emulateRawMode(fal
{ {
_im = 0; _im = 0;
_ic = 0; _ic = 0;
_SelectionOwned=false;
} }
CUnixEventEmitter::~CUnixEventEmitter() CUnixEventEmitter::~CUnixEventEmitter()
@ -47,12 +50,24 @@ void CUnixEventEmitter::init(Display *dpy, Window win, NL3D::IDriver *driver)
_win = win; _win = win;
_driver = driver; _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 TODO: implements all useful events processing
EnterWindowMask|LeaveWindowMask|ButtonMotionMask|Button1MotionMask|Button2MotionMask| EnterWindowMask|LeaveWindowMask|ButtonMotionMask|Button1MotionMask|Button2MotionMask|
Button3MotionMask|Button4MotionMask|Button5MotionMask|KeymapStateMask|ExposureMask| Button3MotionMask|Button4MotionMask|Button5MotionMask|KeymapStateMask|
SubstructureNotifyMask|VisibilityChangeMask|FocusChangeMask|PropertyChangeMask| SubstructureNotifyMask|VisibilityChangeMask|FocusChangeMask|PropertyChangeMask|
ColormapChangeMask|OwnerGrabButtonMask ColormapChangeMask|OwnerGrabButtonMask
*/ */
@ -578,6 +593,67 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server)
} }
break; 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: case FocusIn:
// keyboard focus // keyboard focus
if (_ic) XSetICFocus(_ic); if (_ic) XSetICFocus(_ic);
@ -605,6 +681,180 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server)
return true; 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 } // NLMISC
#endif // defined(NL_OS_UNIX) && !defined(NL_OS_MAC) #endif // defined(NL_OS_UNIX) && !defined(NL_OS_MAC)

View file

@ -65,6 +65,16 @@ public:
*/ */
bool processMessage(XEvent &event, CEventServer *server = NULL); 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:
// Private internal server message // Private internal server message
@ -88,6 +98,7 @@ private:
}; };
void createIM(); void createIM();
bool prepareSelectionContent (Atom selection, Atom requestedFormat, Atom propertyName);
Display* _dpy; Display* _dpy;
Window _win; Window _win;
@ -97,6 +108,9 @@ private:
bool _emulateRawMode; bool _emulateRawMode;
NL3D::IDriver* _driver; NL3D::IDriver* _driver;
CUnixEventServer _InternalServer; CUnixEventServer _InternalServer;
ucstring _CopiedString;
Atom _PrecomputedAtom[6];
bool _SelectionOwned;
}; };

View file

@ -1938,5 +1938,14 @@ bool CDriverUser::setRenderTarget(class UTexture & uTex, uint32 x, uint32 y, uin
return result; return result;
} }
bool CDriverUser::copyTextToClipboard(const ucstring &text)
{
return _Driver->copyTextToClipboard(text);
}
bool CDriverUser::pasteTextFromClipboard(ucstring &text)
{
return _Driver->pasteTextFromClipboard(text);
}
} // NL3D } // NL3D

View file

@ -16,7 +16,7 @@
#include "stdmisc.h" #include "stdmisc.h"
#include "nel/misc/event_emitter_multi.h" #include "nel/misc/event_emitter_multi.h"
#include "nel/misc/system_utils.h"
namespace NLMISC namespace NLMISC
{ {
@ -99,7 +99,17 @@ const IEventEmitter *CEventEmitterMulti::getEmitter(uint index) const
return _Emitters[index].first; 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 } // NLMISC