Changed: #1038 Implement copy/paste for Linux

hg/feature/sound
kervala 14 years ago
parent a044d8a5e7
commit 621f7505c1

@ -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.

@ -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,211 +658,177 @@ bool CUnixEventEmitter::processMessage (XEvent &event, CEventServer *server)
break; break;
} }
case SelectionClear: case SelectionClear:
nlwarning("SelectionClear:");
_SelectionOwned = false; _SelectionOwned = false;
_CopiedString = ""; _CopiedString = "";
break; break;
case FocusIn: case SelectionNotify:
// keyboard focus {
if (_ic) XSetICFocus(_ic); Atom target = event.xselection.target;
break;
case FocusOut:
// keyboard focus
if (_ic) XUnsetICFocus(_ic);
break;
case KeymapNotify:
break;
case MappingNotify:
// update keymap
XRefreshKeyboardMapping((XMappingEvent *)&event);
break;
case DestroyNotify:
// XIM server has crashed
createIM();
break;
default:
// nlinfo("UnknownEvent");
// XtDispatchEvent(&event);
return false;
}
return true; Atom actualType = 0;
} int actualFormat = 0;
unsigned long nitems = 0, bytesLeft = 0;
/** // some applications are sending ATOM and other TARGETS
* This function copy a selection into propertyName. if (target == XA_TARGETS || target == XA_ATOM)
* 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); Atom *supportedTargets = NULL;
XSync(_dpy, False); // 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;
int i = 0; if (bytesLeft > 0)
{
nlwarning("Paste: Supported TARGETS list too long.");
}
bool gotReply = false; Atom bestTarget = 0;
sint bestTargetElect = 0;
do // Elect best type
for (uint i=0; i < nitems; i++)
{ {
XEvent event; // nlwarning(" - Type=%s (%u)", XGetAtomName(_dpy, supportedTargets[i]), (uint)supportedTargets[i]);
usleep(500); if (supportedTargets[i] == XA_UTF8_STRING )
gotReply = XCheckTypedWindowEvent(_dpy, _win, SelectionNotify, &event);
if (gotReply)
{ {
return true; if (bestTargetElect < 2)
} {
i++; bestTarget = XA_UTF8_STRING;
bestTargetElect = 2;
} }
while (i<20);
return false;
} }
else if (supportedTargets[i] == XA_STRING )
bool CUnixEventEmitter::copyTextToClipboard(const ucstring &text)
{
_CopiedString = text;
XSetSelectionOwner (_dpy, XA_CLIPBOARD, _win, CurrentTime);
{ {
Window selectionOwner = XGetSelectionOwner (_dpy, XA_CLIPBOARD); if (bestTargetElect < 1)
if ( selectionOwner != _win )
{ {
nlwarning("Can't aquire selection"); bestTarget = XA_STRING;
return false; bestTargetElect = 1;
}
} }
_SelectionOwned = true;
nlwarning("Owning selection");
return true;
} }
nlwarning("Paste: Can't acquire selection."); XFree(supportedTargets);
if (!bestTargetElect)
{
nlwarning("Paste buffer is not a text buffer.");
return false; return false;
} }
bool CUnixEventEmitter::pasteTextFromClipboard(ucstring &text) // request string conversion
{ XConvertSelection(_dpy, XA_CLIPBOARD, bestTarget, XA_NEL_SEL, _win, CurrentTime);
// check if we own the selection
if (_SelectionOwned)
{
text = _CopiedString;
return true;
} }
else if (target == XA_UTF8_STRING || target == XA_STRING)
Window selectionOwner = XGetSelectionOwner (_dpy, XA_CLIPBOARD);
if (selectionOwner != None)
{ {
Atom *supportedTargets; uint8 *data = NULL;
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 // get selection
bool bres = prepareSelectionContent(XA_CLIPBOARD, XA_TARGETS, XA_NEL_SEL); if (XGetWindowProperty(_dpy, _win, XA_NEL_SEL, 0, XMaxRequestSize(_dpy), False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesLeft, (unsigned char**)&data) != Success)
if (!bres)
{
nlwarning("Paste: Cannot ennumerate TARGETS");
return false; return false;
}
result = XGetWindowProperty(_dpy, _win, XA_NEL_SEL, 0, XMaxRequestSize(_dpy), False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesLeft,(unsigned char**) &supportedTargets); ucstring text;
std::string tmpData = (const char*)data;
XFree(data);
if (result != Success) // convert buffer to ucstring
if (target == XA_UTF8_STRING)
{ {
return false; text = ucstring::makeFromUtf8(tmpData);
} }
else if (target == XA_STRING)
if ( bytesLeft>0 )
{ {
nlwarning("Paste: Supported TARGETS list too long."); // We hope we find what we need in the first packet. text = tmpData;
} }
else
// Elect best type
for (uint i=0; i < nitems; i++)
{ {
nldebug(" - Type=%s", XGetAtomName(_dpy, supportedTargets[i])); nlwarning("Unknow format %u", (uint)target);
}
if (supportedTargets[i] == XA_STRING ) // sent string event to event server
{ server->postEvent (new CEventString (text, this));
if (bestTargetElect < 1)
{
bestTarget = XA_STRING;
bestTargetElect = 1;
} }
else
{
nlwarning("Unknow target %u", (uint)target);
} }
if (supportedTargets[i] == XA_UTF8_STRING ) break;
{
if (bestTargetElect < 2)
{
bestTarget = XA_UTF8_STRING;
bestTargetElect = 2;
} }
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:
break;
case MappingNotify:
// update keymap
XRefreshKeyboardMapping((XMappingEvent *)&event);
break;
case DestroyNotify:
// 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);
return false;
} }
XFree(supportedTargets); return true;
}
if (!bestTargetElect) bool CUnixEventEmitter::copyTextToClipboard(const ucstring &text)
{ {
nlwarning("Paste buffer is not a text buffer."); _CopiedString = text;
return false;
}
// Ask for selection lenght && copy to buffer // NeL window is the owner of clipboard
bres = prepareSelectionContent(XA_CLIPBOARD, bestTarget, XA_NEL_SEL); XSetSelectionOwner(_dpy, XA_CLIPBOARD, _win, CurrentTime);
if (!bres) // check we are owning the clipboard
if (XGetSelectionOwner(_dpy, XA_CLIPBOARD) != _win)
{ {
nlwarning ("Paste: cannot obtain data. Aborting."); nlwarning("Can't aquire selection");
return false; return false;
} }
XGetWindowProperty(_dpy, _win, XA_NEL_SEL, 0, XMaxRequestSize(_dpy), False, AnyPropertyType, &actualType, &actualFormat, &nitems, &bytesLeft,(unsigned char**) &data); _SelectionOwned = true;
std::string tmpData = (const char*)data;
XFree(data);
switch (bestTargetElect)
{
case 1: // XA_STRING
text = tmpData;
return true; return true;
}
case 2: // XA_UTF8_STRING bool CUnixEventEmitter::pasteTextFromClipboard(ucstring &text)
text = ucstring::makeFromUtf8(tmpData); {
// check if we own the selection
if (_SelectionOwned)
{
text = _CopiedString;
return true; return true;
default:
break;
} }
nlwarning("Paste: buffer is not a text buffer."); // check if there is a data in clipboard
} if (XGetSelectionOwner(_dpy, XA_CLIPBOARD) == None)
return false;
nlwarning("Paste: error !"); // request supported methods
XConvertSelection(_dpy, XA_CLIPBOARD, XA_TARGETS, XA_NEL_SEL, _win, CurrentTime);
// 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)

@ -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;
}; };

Loading…
Cancel
Save