Changed: #1038 Implement copy/paste for Linux

This commit is contained in:
kervala 2010-10-17 16:29:18 +02:00
parent a044d8a5e7
commit 621f7505c1
3 changed files with 164 additions and 176 deletions

View file

@ -19,6 +19,7 @@
#include "types_nl.h" #include "types_nl.h"
#include "class_id.h" #include "class_id.h"
#include "ucstring.h"
#include <map> #include <map>
#include <list> #include <list>
@ -58,6 +59,7 @@ protected:
const CClassId EventKeyDownId (0x3c2643da, 0x43f802a1); const CClassId EventKeyDownId (0x3c2643da, 0x43f802a1);
const CClassId EventKeyUpId (0x1e62e85, 0x68a35d46); const CClassId EventKeyUpId (0x1e62e85, 0x68a35d46);
const CClassId EventCharId (0x552255fe, 0x75a2373f); const CClassId EventCharId (0x552255fe, 0x75a2373f);
const CClassId EventStringId (0x49b5af8f, 0x7f52cd26);
// Window events // Window events
const CClassId EventActivateId (0x7da66b0a, 0x1ef74519); 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. * CEventMouse.

View file

@ -29,6 +29,11 @@
typedef bool (*x11Proc)(NL3D::IDriver *drv, XEvent *e); 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 { namespace NLMISC {
CUnixEventEmitter::CUnixEventEmitter ():_dpy(NULL), _win(0), _emulateRawMode(false), _driver(NULL) 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; _driver = driver;
XSelectInput (_dpy, _win, KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask|StructureNotifyMask|ExposureMask); XSelectInput (_dpy, _win, KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask|StructureNotifyMask|ExposureMask);
_PrecomputedAtom[0] = XInternAtom(dpy, "CLIPBOARD", False);
#define XA_CLIPBOARD _PrecomputedAtom[0] // define Atoms used by clipboard
_PrecomputedAtom[1] = XInternAtom(dpy, "UTF8_STRING", False); XA_CLIPBOARD = XInternAtom(dpy, "CLIPBOARD", False);
#define XA_UTF8_STRING _PrecomputedAtom[1] XA_UTF8_STRING = XInternAtom(dpy, "UTF8_STRING", False);
_PrecomputedAtom[2] = XInternAtom(dpy, "TARGETS", False); XA_TARGETS = XInternAtom(dpy, "TARGETS", False);
#define XA_TARGETS _PrecomputedAtom[2] XA_NEL_SEL = XInternAtom(dpy, "NeL_SEL", False);
_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
@ -568,10 +567,23 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server)
#ifdef X_HAVE_UTF8_STRING #ifdef X_HAVE_UTF8_STRING
ucstring ucstr; ucstring ucstr;
ucstr.fromUtf8(Text); 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 #else
for (int i = 0; i < c; i++) 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 #endif
} }
break; break;
@ -612,12 +624,10 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server)
} }
if (req.target == XA_TARGETS) if (req.target == XA_TARGETS)
{ {
nlwarning("Client is asking for TARGETS");
Atom targets[] = Atom targets[] =
{ {
XA_TARGETS, XA_TARGETS,
XA_TEXT, XA_STRING,
XA_UTF8_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 */); 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; respond.xselection.property = req.property;
std::string str = _CopiedString.toString(); std::string str = _CopiedString.toString();
XChangeProperty(req.display, req.requestor, req.property, XA_STRING, 8, PropModeReplace, (const unsigned char*)str.c_str(), str.length()); 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) 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(); 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 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; respond.xselection.property = None;
} }
@ -650,16 +658,111 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server)
break; break;
} }
case SelectionClear: case SelectionClear:
nlwarning("SelectionClear:");
_SelectionOwned = false; _SelectionOwned = false;
_CopiedString = ""; _CopiedString = "";
break; 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: case FocusIn:
// keyboard focus // keyboard focus
server->postEvent (new CEventSetFocus (true, this));
if (_ic) XSetICFocus(_ic); if (_ic) XSetICFocus(_ic);
break; break;
case FocusOut: case FocusOut:
// keyboard focus // keyboard focus
server->postEvent (new CEventSetFocus (false, this));
if (_ic) XUnsetICFocus(_ic); if (_ic) XUnsetICFocus(_ic);
break; break;
case KeymapNotify: case KeymapNotify:
@ -672,6 +775,12 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server)
// XIM server has crashed // XIM server has crashed
createIM(); createIM();
break; break;
case ClientMessage:
if ((xevent.xclient.format == 32) && (xevent.xclient.data.l[0] == videodata->WM_DELETE_WINDOW))
{
server->postEvent (new CEventDestroyWindow (this));
}
break;
default: default:
// nlinfo("UnknownEvent"); // nlinfo("UnknownEvent");
// XtDispatchEvent(&event); // XtDispatchEvent(&event);
@ -681,65 +790,23 @@ 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) bool CUnixEventEmitter::copyTextToClipboard(const ucstring &text)
{ {
_CopiedString = 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); nlwarning("Can't aquire selection");
return false;
if ( selectionOwner != _win )
{
nlwarning("Can't aquire selection");
return false;
}
_SelectionOwned = true;
nlwarning("Owning selection");
return true;
} }
nlwarning("Paste: Can't acquire selection."); _SelectionOwned = true;
return false; return true;
} }
bool CUnixEventEmitter::pasteTextFromClipboard(ucstring &text) bool CUnixEventEmitter::pasteTextFromClipboard(ucstring &text)
@ -751,110 +818,17 @@ bool CUnixEventEmitter::pasteTextFromClipboard(ucstring &text)
return true; 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) // request supported methods
{ XConvertSelection(_dpy, XA_CLIPBOARD, XA_TARGETS, XA_NEL_SEL, _win, CurrentTime);
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 !");
// don't return result now
return false; return false;
} }
} // NLMISC } // NLMISC
#endif // defined(NL_OS_UNIX) && !defined(NL_OS_MAC) #endif // defined(NL_OS_UNIX) && !defined(NL_OS_MAC)

View file

@ -98,7 +98,6 @@ private:
}; };
void createIM(); void createIM();
bool prepareSelectionContent (Atom selection, Atom requestedFormat, Atom propertyName);
Display* _dpy; Display* _dpy;
Window _win; Window _win;
@ -109,7 +108,6 @@ private:
NL3D::IDriver* _driver; NL3D::IDriver* _driver;
CUnixEventServer _InternalServer; CUnixEventServer _InternalServer;
ucstring _CopiedString; ucstring _CopiedString;
Atom _PrecomputedAtom[6];
bool _SelectionOwned; bool _SelectionOwned;
}; };