checkpoint. implementing clipboard owner in x windows.
This commit is contained in:
parent
c19022e965
commit
ff9c3ba7af
|
@ -47,7 +47,10 @@ void CXWindowsSecondaryScreen::run()
|
||||||
// we just lost the selection. that means someone else
|
// we just lost the selection. that means someone else
|
||||||
// grabbed the selection so this screen is now the
|
// grabbed the selection so this screen is now the
|
||||||
// selection owner. report that to the server.
|
// selection owner. report that to the server.
|
||||||
|
if (lostClipboard(xevent.xselectionclear.selection,
|
||||||
|
xevent.xselectionclear.time)) {
|
||||||
m_client->onClipboardChanged();
|
m_client->onClipboardChanged();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SelectionNotify:
|
case SelectionNotify:
|
||||||
|
|
|
@ -121,7 +121,10 @@ void CXWindowsPrimaryScreen::run()
|
||||||
// we just lost the selection. that means someone else
|
// we just lost the selection. that means someone else
|
||||||
// grabbed the selection so this screen is now the
|
// grabbed the selection so this screen is now the
|
||||||
// selection owner. report that to the server.
|
// selection owner. report that to the server.
|
||||||
|
if (lostClipboard(xevent.xselectionclear.selection,
|
||||||
|
xevent.xselectionclear.time)) {
|
||||||
m_server->grabClipboard();
|
m_server->grabClipboard();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SelectionNotify:
|
case SelectionNotify:
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
// CXWindowsScreen
|
// CXWindowsScreen
|
||||||
//
|
//
|
||||||
|
|
||||||
|
static const Atom kClipboardSelection = XA_PRIMARY;
|
||||||
|
static const UInt32 kMaxRequestSize = 4096;
|
||||||
|
|
||||||
CXWindowsScreen::CXWindowsScreen() :
|
CXWindowsScreen::CXWindowsScreen() :
|
||||||
m_display(NULL),
|
m_display(NULL),
|
||||||
m_root(None),
|
m_root(None),
|
||||||
|
@ -53,6 +56,10 @@ void CXWindowsScreen::openDisplay()
|
||||||
// get some atoms
|
// get some atoms
|
||||||
m_atomTargets = XInternAtom(m_display, "TARGETS", False);
|
m_atomTargets = XInternAtom(m_display, "TARGETS", False);
|
||||||
m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False);
|
m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False);
|
||||||
|
m_atomTimestamp = XInternAtom(m_display, "TIMESTAMP", False);
|
||||||
|
m_atomAtom = XInternAtom(m_display, "ATOM", False);
|
||||||
|
m_atomAtomPair = XInternAtom(m_display, "ATOM_PAIR", False);
|
||||||
|
m_atomInteger = XInternAtom(m_display, "INTEGER", False);
|
||||||
m_atomData = XInternAtom(m_display, "DESTINATION", False);
|
m_atomData = XInternAtom(m_display, "DESTINATION", False);
|
||||||
m_atomINCR = XInternAtom(m_display, "INCR", False);
|
m_atomINCR = XInternAtom(m_display, "INCR", False);
|
||||||
m_atomText = XInternAtom(m_display, "TEXT", False);
|
m_atomText = XInternAtom(m_display, "TEXT", False);
|
||||||
|
@ -159,16 +166,29 @@ void CXWindowsScreen::doStop()
|
||||||
m_stop = true;
|
m_stop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CXWindowsScreen::lostClipboard(
|
||||||
|
Atom selection, Time timestamp)
|
||||||
|
{
|
||||||
|
if (selection == kClipboardSelection) {
|
||||||
|
// note the time
|
||||||
|
CLock lock(&m_mutex);
|
||||||
|
m_lostClipboard = timestamp;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool CXWindowsScreen::setDisplayClipboard(
|
bool CXWindowsScreen::setDisplayClipboard(
|
||||||
const IClipboard* clipboard,
|
const IClipboard* clipboard,
|
||||||
Window requestor, Time timestamp)
|
Window requestor, Time timestamp)
|
||||||
{
|
{
|
||||||
CLock lock(&m_mutex);
|
CLock lock(&m_mutex);
|
||||||
|
|
||||||
XSetSelectionOwner(m_display, XA_PRIMARY, requestor, timestamp);
|
XSetSelectionOwner(m_display, kClipboardSelection, requestor, timestamp);
|
||||||
if (XGetSelectionOwner(m_display, XA_PRIMARY) == requestor) {
|
if (XGetSelectionOwner(m_display, kClipboardSelection) == requestor) {
|
||||||
// we got the selection
|
// we got the selection
|
||||||
log((CLOG_DEBUG "grabbed clipboard"));
|
log((CLOG_DEBUG "grabbed clipboard"));
|
||||||
|
m_gotClipboard = timestamp;
|
||||||
|
|
||||||
if (clipboard != NULL) {
|
if (clipboard != NULL) {
|
||||||
// save clipboard to serve requests
|
// save clipboard to serve requests
|
||||||
|
@ -202,9 +222,7 @@ void CXWindowsScreen::getDisplayClipboard(
|
||||||
// in particular, this prevents the event thread from stealing the
|
// in particular, this prevents the event thread from stealing the
|
||||||
// selection notify event we're expecting.
|
// selection notify event we're expecting.
|
||||||
CLock lock(&m_mutex);
|
CLock lock(&m_mutex);
|
||||||
|
Atom selection = kClipboardSelection;
|
||||||
// use PRIMARY selection as the "clipboard"
|
|
||||||
Atom selection = XA_PRIMARY;
|
|
||||||
|
|
||||||
// ask the selection for all the formats it has. some owners return
|
// ask the selection for all the formats it has. some owners return
|
||||||
// the TARGETS atom and some the ATOM atom when TARGETS is requested.
|
// the TARGETS atom and some the ATOM atom when TARGETS is requested.
|
||||||
|
@ -359,7 +377,7 @@ bool CXWindowsScreen::getDisplayClipboard(
|
||||||
data.reserve(size);
|
data.reserve(size);
|
||||||
|
|
||||||
// look for property notify events with the following
|
// look for property notify events with the following
|
||||||
PropertyNotifyInfo filter;
|
CPropertyNotifyInfo filter;
|
||||||
filter.m_window = requestor;
|
filter.m_window = requestor;
|
||||||
filter.m_property = property;
|
filter.m_property = property;
|
||||||
|
|
||||||
|
@ -488,7 +506,7 @@ IClipboard::EFormat CXWindowsScreen::getFormat(Atom src) const
|
||||||
Bool CXWindowsScreen::findSelectionNotify(
|
Bool CXWindowsScreen::findSelectionNotify(
|
||||||
Display*, XEvent* xevent, XPointer arg)
|
Display*, XEvent* xevent, XPointer arg)
|
||||||
{
|
{
|
||||||
Window requestor = *((Window*)arg);
|
Window requestor = *static_cast<Window*>(arg);
|
||||||
return (xevent->type == SelectionNotify &&
|
return (xevent->type == SelectionNotify &&
|
||||||
xevent->xselection.requestor == requestor) ? True : False;
|
xevent->xselection.requestor == requestor) ? True : False;
|
||||||
}
|
}
|
||||||
|
@ -496,7 +514,7 @@ Bool CXWindowsScreen::findSelectionNotify(
|
||||||
Bool CXWindowsScreen::findPropertyNotify(
|
Bool CXWindowsScreen::findPropertyNotify(
|
||||||
Display*, XEvent* xevent, XPointer arg)
|
Display*, XEvent* xevent, XPointer arg)
|
||||||
{
|
{
|
||||||
PropertyNotifyInfo* filter = (PropertyNotifyInfo*)arg;
|
CPropertyNotifyInfo* filter = static_cast<CPropertyNotifyInfo*>(arg);
|
||||||
return (xevent->type == PropertyNotify &&
|
return (xevent->type == PropertyNotify &&
|
||||||
xevent->xproperty.window == filter->m_window &&
|
xevent->xproperty.window == filter->m_window &&
|
||||||
xevent->xproperty.atom == filter->m_property &&
|
xevent->xproperty.atom == filter->m_property &&
|
||||||
|
@ -511,63 +529,139 @@ void CXWindowsScreen::addClipboardRequest(
|
||||||
// mutex the display
|
// mutex the display
|
||||||
CLock lock(&m_mutex);
|
CLock lock(&m_mutex);
|
||||||
|
|
||||||
// check the target
|
// we can only own kClipboardSelection
|
||||||
IClipboard::EFormat format = IClipboard::kNumFormats;
|
if (selection != kClipboardSelection) {
|
||||||
if (selection == XA_PRIMARY) {
|
|
||||||
if (target == m_atomTargets) {
|
|
||||||
// send the list of available targets
|
|
||||||
sendClipboardTargetsNotify(requestor, property, time);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (target == m_atomMultiple) {
|
|
||||||
|
// a request for multiple targets is special
|
||||||
|
if (target == m_atomMultiple) {
|
||||||
// add a multiple request
|
// add a multiple request
|
||||||
if (property != None) {
|
if (property != None) {
|
||||||
addClipboardMultipleRequest(requestor, property, time);
|
if (addClipboardMultipleRequest(requestor, property, time)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
format = getFormat(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we can't handle the format then send a failure notification
|
// bad request or couldn't satisfy request
|
||||||
|
sendNotify(requestor, target, None, time);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle remaining request formats
|
||||||
|
doAddClipboardRequest(requestor, target, property, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CXWindowsScreen::doAddClipboardRequest(
|
||||||
|
Window requestor, Atom target,
|
||||||
|
Atom property, Time time)
|
||||||
|
{
|
||||||
|
// check the target. if we can't handle the format then send
|
||||||
|
// a failure notification.
|
||||||
|
IClipboard::EFormat format = getFormat(target);
|
||||||
if (format == IClipboard::kNumFormats) {
|
if (format == IClipboard::kNumFormats) {
|
||||||
XEvent event;
|
sendNotify(requestor, target, None, time);
|
||||||
event.xselection.type = SelectionNotify;
|
|
||||||
event.xselection.display = m_display;
|
|
||||||
event.xselection.requestor = requestor;
|
|
||||||
event.xselection.selection = selection;
|
|
||||||
event.xselection.target = target;
|
|
||||||
event.xselection.property = None;
|
|
||||||
event.xselection.time = time;
|
|
||||||
XSendEvent(m_display, requestor, False, 0, &event);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we could process short requests without adding to the request
|
// we could process short requests without adding to the request
|
||||||
// queue but we'll keep things simple by doing all requests the
|
// queue but we'll keep things simple by doing all requests the
|
||||||
// same way.
|
// same way.
|
||||||
// FIXME
|
addClipboardRequest(requestor, property, time, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXWindowsScreen::sendClipboardTargetsNotify(
|
bool CXWindowsScreen::sendClipboardData(
|
||||||
|
Window requestor, Atom target,
|
||||||
|
Atom property, Time time)
|
||||||
|
{
|
||||||
|
// FIXME -- caller must send notify event
|
||||||
|
if (target == m_atomTargets) {
|
||||||
|
return sendClipboardTargets(requestor, property, time);
|
||||||
|
}
|
||||||
|
else if (target == m_atomTimestamp) {
|
||||||
|
// FIXME -- handle Alloc errors (by returning false)
|
||||||
|
XChangeProperty(m_display, requestor, property,
|
||||||
|
m_atomInteger, sizeof(m_gotClipboard),
|
||||||
|
PropModeReplace,
|
||||||
|
reinterpret_cast<unsigned char*>(&m_gotClipboard),
|
||||||
|
1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (getFormat(target) != IClipboard::kNumFormats) {
|
||||||
|
// convert clipboard to appropriate format
|
||||||
|
CString data;
|
||||||
|
|
||||||
|
if (data.size() > kMaxRequestSize) {
|
||||||
|
// FIXME -- handle Alloc errors (by returning false)
|
||||||
|
const UInt32 zero = 0;
|
||||||
|
XChangeProperty(m_display, requestor, property,
|
||||||
|
m_atomINCR, sizeof(zero),
|
||||||
|
PropModeReplace,
|
||||||
|
reinterpret_cast<unsigned char*>(&zero),
|
||||||
|
1);
|
||||||
|
// FIXME -- add to request list
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// FIXME -- handle Alloc errors (by returning false)
|
||||||
|
XChangeProperty(m_display, requestor, property,
|
||||||
|
/* FIXME -- use appropriate type */,
|
||||||
|
/* FIXME -- use appropriate size */,
|
||||||
|
PropModeReplace,
|
||||||
|
data.data(), data.size());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CXWindowsScreen::sendClipboardData(CRequestList* list)
|
||||||
|
{
|
||||||
|
// send more data from the request at the head of the queue
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CXWindowsScreen::sendClipboardTargets(
|
||||||
Window requestor,
|
Window requestor,
|
||||||
Atom property, Time time)
|
Atom property, Time time)
|
||||||
{
|
{
|
||||||
|
// count the number of targets, plus TARGETS and MULTIPLE
|
||||||
|
SInt32 numTargets = 2;
|
||||||
|
if (m_clipboard.has(IClipboard::kText)) {
|
||||||
|
numTargets += 1;
|
||||||
|
}
|
||||||
|
|
||||||
// construct response
|
// construct response
|
||||||
// FIXME
|
Atom* response = new Atom[numTargets];
|
||||||
|
SInt32 count = 0;
|
||||||
|
response[count++] = m_atomTargets;
|
||||||
|
response[count++] = m_atomMultiple;
|
||||||
|
if (m_clipboard.has(IClipboard::kText)) {
|
||||||
|
response[count++] = m_atomText;
|
||||||
|
}
|
||||||
|
|
||||||
// set property
|
// send response (we assume we can transfer the entire list at once)
|
||||||
// FIXME
|
// FIXME -- handle Alloc errors (by returning false)
|
||||||
|
XChangeProperty(m_display, requestor, property,
|
||||||
|
m_atomAtom, sizeof(Atom),
|
||||||
|
PropModeReplace,
|
||||||
|
reinterpret_cast<unsigned char*>(response),
|
||||||
|
count);
|
||||||
|
|
||||||
// send notification
|
// done with our response
|
||||||
|
delete[] response;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CXWindowsScreen::sendNotify(
|
||||||
|
Window requestor, Atom target,
|
||||||
|
Atom property, Time time)
|
||||||
|
{
|
||||||
XEvent event;
|
XEvent event;
|
||||||
event.xselection.type = SelectionNotify;
|
event.xselection.type = SelectionNotify;
|
||||||
event.xselection.display = m_display;
|
event.xselection.display = m_display;
|
||||||
event.xselection.requestor = requestor;
|
event.xselection.requestor = requestor;
|
||||||
event.xselection.selection = XA_PRIMARY;
|
event.xselection.selection = kClipboardSelection;
|
||||||
event.xselection.target = m_atomTargets;
|
event.xselection.target = target;
|
||||||
event.xselection.property = property;
|
event.xselection.property = property;
|
||||||
event.xselection.time = time;
|
event.xselection.time = time;
|
||||||
XSendEvent(m_display, requestor, False, 0, &event);
|
XSendEvent(m_display, requestor, False, 0, &event);
|
||||||
|
@ -581,18 +675,51 @@ void CXWindowsScreen::processClipboardRequest(
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXWindowsScreen::addClipboardRequest(
|
void CXWindowsScreen::addClipboardRequest(
|
||||||
Window requestor,
|
Window requestor, Atom target,
|
||||||
Atom property, Time time,
|
Atom property, Time time);
|
||||||
IClipboard::EFormat format)
|
|
||||||
{
|
{
|
||||||
// FIXME
|
// FIXME -- add to queue. send first part.
|
||||||
}
|
}
|
||||||
|
|
||||||
void CXWindowsScreen::addClipboardMultipleRequest(
|
bool CXWindowsScreen::addClipboardMultipleRequest(
|
||||||
Window requestor,
|
Window requestor,
|
||||||
Atom property, Time time)
|
Atom property, Time time)
|
||||||
{
|
{
|
||||||
// FIXME
|
// get the list of requested formats
|
||||||
|
Atom type;
|
||||||
|
SInt32 size;
|
||||||
|
CString data;
|
||||||
|
getData(requestor, property, &type, &size, &data);
|
||||||
|
if (type != m_atomAtomPair) {
|
||||||
|
// unexpected data type
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check each format, replacing ones we can't do with None. set
|
||||||
|
// the property for each to the requested data (for small requests)
|
||||||
|
// or INCR (for large requests).
|
||||||
|
bool updated = false;
|
||||||
|
const Atom none = None;
|
||||||
|
const Atom* request = static_cast<const Atom*>(data.data());
|
||||||
|
UInt32 numRequests = data.size() / (2 * sizeof(Atom));
|
||||||
|
for (UInt32 index = 0; index < numRequests; ++index) {
|
||||||
|
// get request info
|
||||||
|
const Atom target = request[2 * index + 0];
|
||||||
|
const Atom property = request[2 * index + 1];
|
||||||
|
|
||||||
|
// handle target
|
||||||
|
if (property != None &&
|
||||||
|
!sendClipboardData(requestor, target, property, time)) {
|
||||||
|
// couldn't handle target. change property to None.
|
||||||
|
data.replace((2 * index + 1) * sizeof(Atom), sizeof(Atom),
|
||||||
|
static_cast<const char*>(&none), sizeof(none));
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME -- update property if updated is true
|
||||||
|
// FIXME -- send notify. if no formats were allowed then send failure
|
||||||
|
// but return true.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include "CMutex.h"
|
#include "CMutex.h"
|
||||||
#include "BasicTypes.h"
|
#include "BasicTypes.h"
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
class CString;
|
class CString;
|
||||||
|
|
||||||
|
@ -54,6 +56,12 @@ class CXWindowsScreen {
|
||||||
// cause getEvent() to return false immediately and forever after
|
// cause getEvent() to return false immediately and forever after
|
||||||
void doStop();
|
void doStop();
|
||||||
|
|
||||||
|
// call when we lose the clipboard ownership (i.e. when we receive
|
||||||
|
// a SelectionClear event). returns true iff we've actually lost
|
||||||
|
// a selection we care about.
|
||||||
|
bool lostClipboard(Atom selection, Time timestamp);
|
||||||
|
|
||||||
|
// set the contents of the clipboard (i.e. primary selection)
|
||||||
bool setDisplayClipboard(const IClipboard* clipboard,
|
bool setDisplayClipboard(const IClipboard* clipboard,
|
||||||
Window requestor, Time timestamp);
|
Window requestor, Time timestamp);
|
||||||
|
|
||||||
|
@ -80,11 +88,22 @@ class CXWindowsScreen {
|
||||||
virtual void onCloseDisplay() = 0;
|
virtual void onCloseDisplay() = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct PropertyNotifyInfo {
|
struct CPropertyNotifyInfo {
|
||||||
public:
|
public:
|
||||||
Window m_window;
|
Window m_window;
|
||||||
Atom m_property;
|
Atom m_property;
|
||||||
};
|
};
|
||||||
|
struct CClipboardRequest {
|
||||||
|
public:
|
||||||
|
CString m_data;
|
||||||
|
UInt32 m_sent;
|
||||||
|
Window m_requestor;
|
||||||
|
Atom m_property;
|
||||||
|
Atom m_type;
|
||||||
|
int m_format;
|
||||||
|
};
|
||||||
|
typedef std::list<CClipboardRequest*> CRequestList;
|
||||||
|
typedef std::map<Window, CRequestList*> CRequestMap;
|
||||||
|
|
||||||
bool getDisplayClipboard(Atom selection, Atom type,
|
bool getDisplayClipboard(Atom selection, Atom type,
|
||||||
Window requestor, Time timestamp,
|
Window requestor, Time timestamp,
|
||||||
|
@ -98,12 +117,16 @@ class CXWindowsScreen {
|
||||||
static Bool findPropertyNotify(Display*,
|
static Bool findPropertyNotify(Display*,
|
||||||
XEvent* xevent, XPointer arg);
|
XEvent* xevent, XPointer arg);
|
||||||
|
|
||||||
void sendClipboardTargetsNotify(Window requestor,
|
void sendClipboardData(CRequestList*);
|
||||||
|
void sendClipboardData(Window requestor, Atom target,
|
||||||
Atom property, Time time);
|
Atom property, Time time);
|
||||||
void addClipboardRequest(Window requestor,
|
void sendClipboardTargets(Window requestor,
|
||||||
Atom property, Time time,
|
Atom property, Time time);
|
||||||
IClipboard::EFormat);
|
void sendNotify(Window requestor, Atom target,
|
||||||
void addClipboardMultipleRequest(Window requestor,
|
Atom property, Time time);
|
||||||
|
void addClipboardRequest(Window requestor, Atom target,
|
||||||
|
Atom property, Time time);
|
||||||
|
bool addClipboardMultipleRequest(Window requestor,
|
||||||
Atom property, Time time);
|
Atom property, Time time);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -116,6 +139,10 @@ class CXWindowsScreen {
|
||||||
// atoms we'll need
|
// atoms we'll need
|
||||||
Atom m_atomTargets;
|
Atom m_atomTargets;
|
||||||
Atom m_atomMultiple;
|
Atom m_atomMultiple;
|
||||||
|
Atom m_atomTimestamp;
|
||||||
|
Atom m_atomAtom;
|
||||||
|
Atom m_atomAtomPair;
|
||||||
|
Atom m_atomInteger;
|
||||||
Atom m_atomData;
|
Atom m_atomData;
|
||||||
Atom m_atomINCR;
|
Atom m_atomINCR;
|
||||||
Atom m_atomText;
|
Atom m_atomText;
|
||||||
|
@ -124,6 +151,13 @@ class CXWindowsScreen {
|
||||||
// the contents of our selection
|
// the contents of our selection
|
||||||
CClipboard m_clipboard;
|
CClipboard m_clipboard;
|
||||||
|
|
||||||
|
// when we got the selection and when we lost it
|
||||||
|
Time m_gotClipboard;
|
||||||
|
Time m_lostClipboard;
|
||||||
|
|
||||||
|
// the request queues
|
||||||
|
CRequestMap m_requests;
|
||||||
|
|
||||||
// X is not thread safe
|
// X is not thread safe
|
||||||
CMutex m_mutex;
|
CMutex m_mutex;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue