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:
parent
42d5c34c64
commit
b045f9506b
@ -68,6 +68,7 @@ public:
|
||||
|
||||
private:
|
||||
DECLARE_DYNAMIC_CLASS(wxClipboard)
|
||||
|
||||
};
|
||||
|
||||
#endif // wxUSE_CLIPBOARD
|
||||
|
@ -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:
|
||||
{
|
||||
|
@ -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 )
|
||||
{
|
||||
XFlush(xdisplay);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#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);
|
||||
GetClipboardData(xdisplay, window, data, dfarr[i]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
// wxUSE_CLIPBOARD
|
||||
|
Loading…
Reference in New Issue
Block a user