Rewrite the wxX11 cliboard

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@77848 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2014-09-23 17:43:44 +00:00
parent 42d5c34c64
commit b045f9506b
3 changed files with 369 additions and 76 deletions

View File

@ -68,6 +68,7 @@ public:
private:
DECLARE_DYNAMIC_CLASS(wxClipboard)
};
#endif // wxUSE_CLIPBOARD

View File

@ -38,6 +38,7 @@
#include "wx/thread.h"
#endif
#include "wx/clipbrd.h"
#include "wx/x11/private.h"
#include <string.h>
@ -59,6 +60,11 @@ static wxSize g_initialSize = wxDefaultSize;
static wxWindow *g_nextFocus = NULL;
static wxWindow *g_prevFocus = NULL;
//------------------------------------------------------------------------
// X11 clipboard event handling
//------------------------------------------------------------------------
extern "C" void wxClipboardHandleSelectionRequest(XEvent event);
//------------------------------------------------------------------------
// X11 error handling
//------------------------------------------------------------------------
@ -500,6 +506,11 @@ bool wxApp::ProcessXEvent(WXEvent* _event)
}
return false;
}
case SelectionRequest:
{
//A request to paste has occured.
wxClipboardHandleSelectionRequest(*event);
}
#if 0
case DestroyNotify:
{

View File

@ -19,8 +19,140 @@
#include "wx/utils.h"
#include "wx/dataobj.h"
#endif
#include "wx/x11/private.h"
#include "wx/scopedarray.h"
// for store the formats that in a wxDataObject
typedef wxScopedArray<wxDataFormat> wxDataFormatScopedArray;
// ----------------------------------------------
// The mechanism of clipboard under X11 platform:
// ----------------------------------------------
// X11 platform doesn't has a global clipboard like Windows by default. So the
// first problem is the application doing the pasting has to first know where
// to get the data from.
// It solved by X server: The copied data is store in host program(src
// program) it self. When other program(dest program) want to paste data that
// copied in the src program. It will send an paste request, in x11, it will
// send an event which type is SelectionRequest by XConvertSelection, then X
// server will try to find a program that could handle SelectionRequest event.
// In src program, when recieve a SelectionRequest event, it will set the
// copied data to dest program's window property. More specific, the dest
// program is called "requestor", the src program could find which window ask
// for the data, through the event member : "event.xselection.requestor".
// Esentially, the pasting application asks for a list of available formats,
// and then picks the one it deems most suitable.
// -------------------------
// X11 clipboard background:
// -------------------------
// -----
// Atoms
// -----
// Atom is a 4 byte integer ID that used to identify Properties and
// Selections. Properties and Selection have name. But use Atom is more
// efficiency, because it just needs to pass and compare integer ID rather
// than a character string.
// XInterAtom() can gets the atom numer corresponding to a string.
// ie: Atom a = XInternAtom(xdisplay, "CLIPBOARD", True);
// XGetAtomName gets the string corresponding ot the atom number.
// ie: string clip = XGetAtomName(disp, a);
// The content of clip will be "CLIPBOARD".
// ----------
// Properties
// ----------
// Each window has a list of properties. A property is a collection of named,
// typed data. Atom is their name. This is mean the property has an 4 byte
// integer ID and a ASCII string name that store in the correspongding Atom.
//Window have predefined properties, and users can define custom property.
// ----------
// Selections
// ----------
// Selection IS property, just renamed it because it used for exchange data
// between applications.
// Under x11, there have tow useful selections: PRIMARY and CLIPBOARD. PRIMARY
// is for highlight/middle click, CLIPBOARD is for Ctrl+C/Ctrl+V.
// For compability to other wx ports, here use CLIPBOARD Atom.
// ------------------------------------
// X11 clipboard implementation details
// ------------------------------------
// For x11, the first thing is define XA_CLIPBOARD atoem. Although
// X11/Xatom.h have some predefined Atoms, but for some unknown reasons,
// XA_CLIPBOARD is not an predefined Atom.
// ie. XA_CLIPBOARD = XInternAtom(xdisplay, "CLIPBOARD", True);
// The copied data is store in this Atom/Selection.
// -----
// Paste
// -----
// For paste data. First, we need to know which window owns the XA_CLIPBOAD
// selection: win = XGetSelectionOwner(xdisplay, XA_CLIPBOARD);
// If find a window that owns XA_CLIPBOARD, convert the selection to a prop
// atom: XConvertSelection(xdisplay, XA_CLIPBOARD, XA_STRING, XA_CLIPBOARD,
// win, CurrentTime);
// Then get the data in owner's selection through XGetWIndowProperty().
// ----
// Copy
// ----
// If we want copy data to clipboard. We must own the XA_CLIPBOARD selection
// through XSetSelectionOwner().
// But the data is still host by src program. When src program recieve
// SelectionRequest event type. It set the data to requestor's window
// property, through XCHangeProperty(xdisplay, requestor, ...). The second
// parameter is the requests window. Requestor could find through XEvent.
// --------------------
// wxX11 implementation
// --------------------
// According the descirption in above. The implementation of clipboard should
// accomlish these things: set wxDataObject to clipboard, get data from
// clipboard and store it to a wxDataObject.
// -----------------
// SetData function:
// -----------------
// In SetData, due to x11 program does not send data to a gloabal clipboard,
// so the program hold the data, when the program recieve a SelectionRequest
// event, the program set the data to requestor's window property. So in the
// implementation of SetData, it hold the wxDataObject that need to be paste.
// And set XA_CLIPBOARD selection owner.
// -----------------------------------
// wxClipboardHandleSelectionRequest()
// -----------------------------------
// Add a new function void wxClipboardHandleSelectionRequest(XEvent event), it
// called by wxApp::ProcessXEvent(), when other program want to paste data.
// wxApp will check the wxClipboard whether it has data that conforms with
// the format. If has, set the data to requestor's window property.
// The GetData function
// GetData function retieve the data in the owner of XA_CLIPBOARD.
//-----------------------------------------------------------------------------
// data
@ -84,6 +216,200 @@ static void InitX11Clipboard()
}
}
// get the data from XA_CLIPBOARD with specific Atom type.
// currently the 3rd parameter is always XA_CLIPBOARD.
// this is because XA_PRIMARY is not often used.
// the 4th parameter is the format that requestor want get.
unsigned char *GetClipboardDataByFormat(Display* disp, Window win, Atom clipbrdType, Atom target,
unsigned long *length)
{
// some variables that used to get the data in window property
unsigned char *clipbrddata = NULL;
int read_bytes = 1024;
Atom type;
int format, result;
unsigned long bytes_after;
XConvertSelection(disp, XA_CLIPBOARD, target, XA_CLIPBOARD, win, CurrentTime);
do
{
if ( clipbrddata != 0 )
{
XFree(clipbrddata);
clipbrddata = NULL;
}
result = XGetWindowProperty(disp, win, clipbrdType, 0, read_bytes, False, AnyPropertyType,
&type, &format, length, &bytes_after, &clipbrddata);
read_bytes *= 2;
} while ( bytes_after != 0 );
// if we got any data, copy it.
if ( result == Success && clipbrddata )
return clipbrddata;
return NULL;
}
// get the data for a specific wxDataFormat that stored as a property in Root window
void GetClipboardData(Display* disp, Window win, wxDataObject &data, wxDataFormat dfFormat)
{
unsigned char *clipbrdData = NULL;
// some variables that used to get the data in window property
char *text = NULL;
unsigned long len;
switch ( dfFormat )
{
case wxDF_INVALID:
{
return;
}
case wxDF_BITMAP:
{
Atom atomArray[] = {XA_IMAGE_BMP, XA_IMAGE_JPG, XA_IMAGE_TIFF, XA_IMAGE_PNG};
int i;
// check the four atoms in clipboard, try to find whether there has data
// stored in one of these atom.
for ( i = 0; i <= 4; i++ )
{
clipbrdData = GetClipboardDataByFormat(disp, win, XA_CLIPBOARD, atomArray[i], &len);
if ( clipbrdData != NULL )
break;
}
// if we got any data, copy it.
if ( clipbrdData )
{
text = strdup((char*) clipbrdData);
data.SetData(dfFormat, len, text);
}
break;
}
default:
{
clipbrdData = GetClipboardDataByFormat(disp, win, XA_CLIPBOARD, XA_UTF8_STRING, &len);
// if we got any data, copy it.
if ( clipbrdData )
{
text = strdup((char*) clipbrdData );
data.SetData(dfFormat, len, text);
}
}
}
}
// This function is used to response the paste request.
// It convert the stored data into a acceptable format by destination
// program and send an acknowledgement.
// In x11, the "copied" data stored by original window,
// when a paste request arrived, then the original window will send the
// data to destination.
extern "C" void wxClipboardHandleSelectionRequest(XEvent event)
{
if( event.type != SelectionRequest )
return;
//Extract the relavent data
Atom target = event.xselectionrequest.target;
Atom property = event.xselectionrequest.property;
Window requestor = event.xselectionrequest.requestor;
Time timestamp = event.xselectionrequest.time;
Display* disp = event.xselection.display;
// A selection request has arrived, we should know these values:
// Selection atom
// Target atom: the format that requetor want to;
// Property atom: ;
// Requestor: the window that want to paste data.
//Replies to the application requesting a pasting are XEvenst
//sent via XSendEvent
XEvent response;
//Start by constructing a refusal request.
response.xselection.type = SelectionNotify;
//s.xselection.serial - filled in by server
//s.xselection.send_event - filled in by server
//s.xselection.display - filled in by server
response.xselection.requestor = requestor;
response.xselection.selection = XA_CLIPBOARD;
response.xselection.target = target;
response.xselection.property = None; //This means refusal
response.xselection.time = timestamp;
// get formats count in the wxDataObject
// for each data format, search it in x11 selection
// and store it to wxDataObject
wxDataObject* m_data = wxTheClipboard->m_data;
size_t count = m_data->GetFormatCount(wxDataObject::Get);
wxDataFormatScopedArray dfarr(count);
m_data->GetAllFormats(dfarr.get(), wxDataObject::Get);
// retrieve the data with specific image Atom.
Atom atomArray[] = {XA_IMAGE_BMP, XA_IMAGE_JPG, XA_IMAGE_TIFF, XA_IMAGE_PNG};
#if wxUSE_UNICODE
wxDataFormat dfFormat = wxDF_UNICODETEXT;
#else
wxDataFormat dfFormat = wxDF_TEXT;
#endif
int i;
for ( i = 0; i <= 4; i++ )
{
if ( target == atomArray[i] )
{
dfFormat = wxDF_BITMAP;
break;
}
}
// Tell the requestor what we can provide
if ( target == XA_TARGETS )
{
Atom possibleTargets[] =
{
XA_UTF8_STRING,
XA_IMAGE_BMP,
XA_IMAGE_JPG,
XA_IMAGE_TIFF,
XA_IMAGE_PNG,
XA_TEXT_URI_LIST,
XA_TEXT_URI,
XA_TEXT_PLAIN,
XA_TEXT,
XA_STRING,
};
XChangeProperty(disp, requestor, property, XA_ATOM, 32, PropModeReplace,
(unsigned char *)possibleTargets, 2);
}
// the requested target is in possibleTargets
// TODO, when finish the event process issue, improve the check.
else if ( target == XA_UTF8_STRING )
{
unsigned char* buf = NULL;
size_t size = 0;
size = m_data->GetDataSize(dfFormat);
buf = (unsigned char*)malloc(size);
m_data->GetDataHere(dfFormat, buf);
XChangeProperty(disp, requestor, XA_CLIPBOARD, target, 8, PropModeReplace,
buf, size);
delete buf;
}
else
{
// we could not provide the data with a target that requestor want.
return;
}
//Reply
XSendEvent(disp, event.xselectionrequest.requestor, True, 0, &response);
}
//-----------------------------------------------------------------------------
// wxClipboard
//-----------------------------------------------------------------------------
@ -171,38 +497,33 @@ bool wxClipboard::AddData( wxDataObject *data )
/* we can only store one wxDataObject */
Clear();
// in x11, the "copied data" hold by the program itself.
// so here just use m_data to hold the "copied data"
// use wxApp->ProcessXEvent to check whether there has
// SelectionRequest event arrived. If the event arrived,
// check the request format, if wx program has the request
// format, reply the data.
// Reply the data means fill up the data in requestor's
// window property.
// See HandleSelectionRequest for more details
m_data = data;
wxTextDataObject* textdata = (wxTextDataObject*)data;
// prepare and find the root window,
// the copied data stored in the root window as window property
Display* xdisplay = wxGlobalDisplay();
int xscreen = DefaultScreen(xdisplay);
Window window = RootWindow(xdisplay, xscreen);
// Send the data to "clipboard".
// "clipboard" means root window property
XChangeProperty(xdisplay, window, XA_CLIPBOARD, XA_CLIPBOARD, 8, PropModeReplace,
(const unsigned char*)textdata->GetText().mb_str().data(),
textdata->GetTextLength());
size_t size = m_data->GetDataSize(wxDF_UNICODETEXT);
unsigned char* buf = (unsigned char*)malloc(size);
m_data->GetDataHere(wxDF_UNICODETEXT, buf);
Window selectionOwner;
XChangeProperty(xdisplay, window, XA_CLIPBOARD, XA_STRING, 8, PropModeReplace,
buf, size);
// Set this window as the owner of the CLIPBOARD atom
XSetSelectionOwner(xdisplay, XA_CLIPBOARD, window, CurrentTime);
// Check if we accuired ownershop or not
selectionOwner = XGetSelectionOwner(xdisplay, XA_CLIPBOARD);
// Got ownership
if ( selectionOwner == window )
{
return true;
}
else
{
return false;
}
XFlush(xdisplay);
return true;
#endif
}
@ -228,67 +549,27 @@ bool wxClipboard::IsSupported( const wxDataFormat& format )
bool wxClipboard::GetData( wxDataObject& data )
{
// get formats count in the wxDataObject
// for each data format, search it in x11 selection
// and store it to wxDataObject
size_t count = data.GetFormatCount(wxDataObject::Get);
wxDataFormatScopedArray dfarr(count);
data.GetAllFormats(dfarr.get(), wxDataObject::Get);
// prepare and find the root window,
// the copied data stored in the root window as window property
Display* xdisplay = wxGlobalDisplay();
int xscreen = DefaultScreen(xdisplay);
Window window = RootWindow(xdisplay, xscreen);
// the window that hold the copied data.
// this window just indicate whether there has data copied
Window selectionOwner;
// Get the owner of the clipboard selection
selectionOwner = XGetSelectionOwner(xdisplay, XA_CLIPBOARD);
// some variables that used to get the data in window property
char *text = NULL;
Atom type;
unsigned char* textdata = NULL;
int format, result;
unsigned long len, bytesLeft, dummy;
// there has something in the "clipboard"
if ( selectionOwner != None )
// retrieve the data in each format.
for( size_t i = 0; i < count; ++i )
{
XConvertSelection( xdisplay, XA_CLIPBOARD, XA_UTF8_STRING,
XA_CLIPBOARD, window, CurrentTime);
XGetWindowProperty(xdisplay, window, XA_CLIPBOARD, 0, 0, False, AnyPropertyType, &type,
&format, &len, &bytesLeft, &textdata);
if ( textdata )
{
XFree(textdata);
textdata = NULL;
}
if ( bytesLeft )
{
// Fetch the data
result = XGetWindowProperty(xdisplay,
window,
XA_CLIPBOARD, 0,
bytesLeft, False, AnyPropertyType,
&type, &format, &len, &dummy, &textdata);
// if we got any data, copy it.
if ( result == Success )
{
text = strdup((char*) textdata);
XFree(textdata);
XDeleteProperty(xdisplay, window, XA_CLIPBOARD);
if ( data.IsSupported(wxDF_UNICODETEXT))
{
data.SetData(wxDF_UNICODETEXT, len, text);
}
return true;
}
}
GetClipboardData(xdisplay, window, data, dfarr[i]);
}
return false;
return true;
}
#endif
// wxUSE_CLIPBOARD