Unverified Commit 5405447e authored by Mike Gabriel's avatar Mike Gabriel

Merge branch 'uli42-pr/clipboard_overhaul' into 3.6.x

parents 3a3a3373 72c02240
...@@ -90,6 +90,7 @@ static char *nxagentAtomNames[NXAGENT_NUMBER_OF_ATOMS + 1] = ...@@ -90,6 +90,7 @@ static char *nxagentAtomNames[NXAGENT_NUMBER_OF_ATOMS + 1] =
"UTF8_STRING", /* 12 */ "UTF8_STRING", /* 12 */
"_NET_WM_STATE", /* 13 */ "_NET_WM_STATE", /* 13 */
"_NET_WM_STATE_FULLSCREEN", /* 14 */ "_NET_WM_STATE_FULLSCREEN", /* 14 */
"NX_CUT_BUFFER_CLIENT", /* 15 */
NULL, NULL,
NULL NULL
}; };
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
#include "../../include/window.h" #include "../../include/window.h"
#include "screenint.h" #include "screenint.h"
#define NXAGENT_NUMBER_OF_ATOMS 16 #define NXAGENT_NUMBER_OF_ATOMS 17
extern Atom nxagentAtoms[NXAGENT_NUMBER_OF_ATOMS]; extern Atom nxagentAtoms[NXAGENT_NUMBER_OF_ATOMS];
......
...@@ -68,7 +68,9 @@ extern Selection *CurrentSelections; ...@@ -68,7 +68,9 @@ extern Selection *CurrentSelections;
int nxagentLastClipboardClient = -1; int nxagentLastClipboardClient = -1;
static int agentClipboardStatus; static int agentClipboardStatus;
#ifdef DEBUG
static int clientAccum; static int clientAccum;
#endif
Atom serverCutProperty; Atom serverCutProperty;
Atom clientCutProperty; Atom clientCutProperty;
...@@ -80,18 +82,21 @@ const int nxagentMaxSelections = 2; ...@@ -80,18 +82,21 @@ const int nxagentMaxSelections = 2;
typedef struct _SelectionOwner typedef struct _SelectionOwner
{ {
Atom selection; Atom selection; /* _external_ Atom */
ClientPtr client; ClientPtr client; /* internal client */
Window window; Window window; /* internal window id */
WindowPtr windowPtr; WindowPtr windowPtr; /* internal window struct */
Time lastTimeChanged; Time lastTimeChanged; /* internal time */
} SelectionOwner; } SelectionOwner;
/*
* this contains the last selection owner in nxagent. The
* lastTimeChanged is always an internal time. If .client is NULL the
* owner is outside nxagent. .selection will _always_ contain the
* external atom of the selection
*/
static SelectionOwner *lastSelectionOwner; static SelectionOwner *lastSelectionOwner;
static Atom nxagentLastRequestedSelection; static Atom nxagentLastRequestedSelection;
static Atom nxagentClipboardAtom;
static Atom nxagentTimestampAtom;
/* /*
* Needed to handle the notify selection event to * Needed to handle the notify selection event to
...@@ -126,8 +131,10 @@ static Atom lastServerTarget; ...@@ -126,8 +131,10 @@ static Atom lastServerTarget;
static Time lastServerTime; static Time lastServerTime;
static Atom serverTARGETS; static Atom serverTARGETS;
static Atom serverTIMESTAMP;
static Atom serverTEXT; static Atom serverTEXT;
static Atom serverUTF8_STRING; static Atom serverUTF8_STRING;
static Atom serverClientCutProperty;
static Atom clientTARGETS; static Atom clientTARGETS;
static Atom clientTEXT; static Atom clientTEXT;
static Atom clientCOMPOUND_TEXT; static Atom clientCOMPOUND_TEXT;
...@@ -158,6 +165,8 @@ const char * GetClientSelectionStageString(int stage) ...@@ -158,6 +165,8 @@ const char * GetClientSelectionStageString(int stage)
} }
#define SetClientSelectionStage(stage) do {fprintf(stderr, "%s: Changing selection stage from [%s] to [%s]\n", __func__, GetClientSelectionStageString(lastClientStage), GetClientSelectionStageString(SelectionStage##stage)); lastClientStage = SelectionStage##stage;} while (0) #define SetClientSelectionStage(stage) do {fprintf(stderr, "%s: Changing selection stage from [%s] to [%s]\n", __func__, GetClientSelectionStageString(lastClientStage), GetClientSelectionStageString(SelectionStage##stage)); lastClientStage = SelectionStage##stage;} while (0)
#define PrintClientSelectionStage() do {fprintf(stderr, "%s: Current selection stage [%s]\n", __func__, GetClientSelectionStageString(lastClientStage));} while (0) #define PrintClientSelectionStage() do {fprintf(stderr, "%s: Current selection stage [%s]\n", __func__, GetClientSelectionStageString(lastClientStage));} while (0)
#define WINDOWID(ptr) (ptr) ? (ptr->drawable.id) : 0
#define CLINDEX(clientptr) (clientptr) ? (clientptr->index) : -1
#else #else
#define SetClientSelectionStage(stage) do {lastClientStage = SelectionStage##stage;} while (0) #define SetClientSelectionStage(stage) do {lastClientStage = SelectionStage##stage;} while (0)
#define PrintClientSelectionStage() #define PrintClientSelectionStage()
...@@ -201,11 +210,13 @@ XFixesAgentInfoRec nxagentXFixesInfo = { -1, -1, -1, 0 }; ...@@ -201,11 +210,13 @@ XFixesAgentInfoRec nxagentXFixesInfo = { -1, -1, -1, 0 };
extern Display *nxagentDisplay; extern Display *nxagentDisplay;
Bool nxagentValidServerTargets(Atom target); Bool nxagentValidServerTargets(Atom target);
void nxagentSendSelectionNotify(Atom property); static void endTransfer(Bool success);
#define SELECTION_SUCCESS True
#define SELECTION_FAULT False
void nxagentTransferSelection(int resource); void nxagentTransferSelection(int resource);
void nxagentCollectPropertyEvent(int resource); void nxagentCollectPropertyEvent(int resource);
void nxagentResetSelectionOwner(void); void nxagentResetSelectionOwner(void);
WindowPtr nxagentGetClipboardWindow(Atom property, WindowPtr pWin); WindowPtr nxagentGetClipboardWindow(Atom property);
void nxagentNotifyConvertFailure(ClientPtr client, Window requestor, void nxagentNotifyConvertFailure(ClientPtr client, Window requestor,
Atom selection, Atom target, Time time); Atom selection, Atom target, Time time);
int nxagentSendNotify(xEvent *event); int nxagentSendNotify(xEvent *event);
...@@ -222,16 +233,19 @@ void nxagentPrintSelectionStat(int sel) ...@@ -222,16 +233,19 @@ void nxagentPrintSelectionStat(int sel)
#ifdef CLIENTIDS #ifdef CLIENTIDS
fprintf(stderr, " lastSelectionOwner[].client [%p] index [%d] PID [%d] Cmd [%s]\n", fprintf(stderr, " lastSelectionOwner[].client [%p] index [%d] PID [%d] Cmd [%s]\n",
(void *)lOwner.client, (void *)lOwner.client,
lOwner.client ? lOwner.client->index : -1, CLINDEX(lOwner.client),
GetClientPid(lOwner.client), GetClientPid(lOwner.client),
GetClientCmdName(lOwner.client)); GetClientCmdName(lOwner.client));
#else #else
fprintf(stderr, " lastSelectionOwner[].client [%p] index [%d]\n", fprintf(stderr, " lastSelectionOwner[].client [%p] index [%d]\n",
(void *)lOwner.client, (void *)lOwner.client,
lOwner.client ? lOwner.client->index : -1); CLINDEX(lOwner.client));
#endif #endif
fprintf(stderr, " lastSelectionOwner[].window [0x%x]\n", lOwner.window); fprintf(stderr, " lastSelectionOwner[].window [0x%x]\n", lOwner.window);
fprintf(stderr, " lastSelectionOwner[].windowPtr [%p]\n", (void *)lOwner.windowPtr); if (lOwner.windowPtr)
fprintf(stderr, " lastSelectionOwner[].windowPtr [%p] ([0x%x]\n", (void *)lOwner.windowPtr, WINDOWID(lOwner.windowPtr));
else
fprintf(stderr, " lastSelectionOwner[].windowPtr -\n");
fprintf(stderr, " lastSelectionOwner[].lastTimeChanged [%u]\n", lOwner.lastTimeChanged); fprintf(stderr, " lastSelectionOwner[].lastTimeChanged [%u]\n", lOwner.lastTimeChanged);
/* /*
...@@ -250,13 +264,13 @@ void nxagentPrintSelectionStat(int sel) ...@@ -250,13 +264,13 @@ void nxagentPrintSelectionStat(int sel)
#ifdef CLIENTIDS #ifdef CLIENTIDS
fprintf(stderr, " CurrentSelections[].client [%p] index [%d] PID [%d] Cmd [%s]\n", fprintf(stderr, " CurrentSelections[].client [%p] index [%d] PID [%d] Cmd [%s]\n",
(void *)curSel.client, (void *)curSel.client,
curSel.client ? curSel.client->index : -1, CLINDEX(curSel.client),
GetClientPid(curSel.client), GetClientPid(curSel.client),
GetClientCmdName(curSel.client)); GetClientCmdName(curSel.client));
#else #else
fprintf(stderr, " CurrentSelections[].client [%p] index [%d]\n", fprintf(stderr, " CurrentSelections[].client [%p] index [%d]\n",
(void *)curSel.client, (void *)curSel.client,
curSel.client ? curSel.client->index : -1); CLINDEX(curSel.client);
#endif #endif
fprintf(stderr, " CurrentSelections[].window [0x%x]\n", curSel.window); fprintf(stderr, " CurrentSelections[].window [0x%x]\n", curSel.window);
return; return;
...@@ -298,7 +312,10 @@ void nxagentPrintClipboardStat(char *header) ...@@ -298,7 +312,10 @@ void nxagentPrintClipboardStat(char *header)
fprintf(stderr, " lastServerTime (Time) [%u]\n", lastServerTime); fprintf(stderr, " lastServerTime (Time) [%u]\n", lastServerTime);
fprintf(stderr, "lastClient\n"); fprintf(stderr, "lastClient\n");
fprintf(stderr, " lastClientWindowPtr (WindowPtr) [%p]\n", (void *)lastClientWindowPtr); if (lastClientWindowPtr)
fprintf(stderr, " lastClientWindowPtr (WindowPtr) [%p] ([0x%x])\n", (void *)lastClientWindowPtr, WINDOWID(lastClientWindowPtr));
else
fprintf(stderr, " lastClientWindowPtr (WindowPtr) -\n");
fprintf(stderr, " lastClientClientPtr (ClientPtr) [%p]\n", (void *)lastClientClientPtr); fprintf(stderr, " lastClientClientPtr (ClientPtr) [%p]\n", (void *)lastClientClientPtr);
fprintf(stderr, " lastClientRequestor (Window) [0x%x]\n", lastClientRequestor); fprintf(stderr, " lastClientRequestor (Window) [0x%x]\n", lastClientRequestor);
fprintf(stderr, " lastClientProperty (Atom) [% 4d][%s]\n", lastClientProperty, NameForAtom(lastClientProperty)); fprintf(stderr, " lastClientProperty (Atom) [% 4d][%s]\n", lastClientProperty, NameForAtom(lastClientProperty));
...@@ -323,11 +340,11 @@ void nxagentPrintClipboardStat(char *header) ...@@ -323,11 +340,11 @@ void nxagentPrintClipboardStat(char *header)
fprintf(stderr, " serverUTF8_STRING [% 4d][%s]\n", serverUTF8_STRING, s); fprintf(stderr, " serverUTF8_STRING [% 4d][%s]\n", serverUTF8_STRING, s);
SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverCutProperty); SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverCutProperty);
fprintf(stderr, " serverCutProperty [% 4d][%s]\n", serverCutProperty, s); fprintf(stderr, " serverCutProperty [% 4d][%s]\n", serverCutProperty, s);
SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverClientCutProperty);
fprintf(stderr, " serverClientCutProperty [% 4d][%s]\n", serverClientCutProperty, s);
SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, nxagentClipboardAtom); SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverTIMESTAMP);
fprintf(stderr, " nxagentClipboardAtom [% 4d][%s]\n", nxagentClipboardAtom, s); fprintf(stderr, " serverTIMESTAMP [% 4d][%s]\n", serverTIMESTAMP, s);
SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, nxagentTimestampAtom);
fprintf(stderr, " nxagentTimestampAtom [% 4d][%s]\n", nxagentTimestampAtom, s);
fprintf(stderr, "Atoms (inside nxagent)\n"); fprintf(stderr, "Atoms (inside nxagent)\n");
fprintf(stderr, " clientTARGETS [% 4d][%s]\n", clientTARGETS, NameForAtom(clientTARGETS)); fprintf(stderr, " clientTARGETS [% 4d][%s]\n", clientTARGETS, NameForAtom(clientTARGETS));
...@@ -352,6 +369,85 @@ int GetWindowProperty(WindowPtr pWin, Atom property, long longOffset, long longL ...@@ -352,6 +369,85 @@ int GetWindowProperty(WindowPtr pWin, Atom property, long longOffset, long longL
unsigned long *nItems, unsigned long *bytesAfter, unsigned long *nItems, unsigned long *bytesAfter,
unsigned char **propData); unsigned char **propData);
/*
* Send a SelectionNotify event to the real X server and do some error
* handling (in DEBUG mode)
*/
Status SendSelectionNotifyEventToServer(XSelectionEvent *event_to_send)
{
Window w = event_to_send->requestor;
event_to_send->type = SelectionNotify;
event_to_send->send_event = True;
event_to_send->display = nxagentDisplay;
Status result = XSendEvent(nxagentDisplay, w, False, 0L, (XEvent *)event_to_send);
#ifdef DEBUG
/*
* man XSendEvent: XSendEvent returns zero if the conversion to wire
* protocol format failed and returns nonzero otherwise. XSendEvent
* can generate BadValue and BadWindow errors.
*/
if (result == 0)
{
fprintf(stderr, "%s: XSendEvent to [0x%x] failed.\n", __func__, w);
}
else
{
if (result == BadValue || result == BadWindow)
{
fprintf(stderr, "%s: WARNING! XSendEvent to [0x%x] failed: %s\n", __func__, w, GetXErrorString(result));
}
else
{
fprintf(stderr, "%s: XSendEvent() successfully sent to [0x%x]\n", __func__, w);
}
}
#endif
//NXFlushDisplay(nxagentDisplay, NXFlushLink);
return result;
}
int SendEventToClient(ClientPtr client, xEvent *pEvents)
{
return TryClientEvents (client, pEvents, 1, NoEventMask, NoEventMask, NullGrab);
}
int SendSelectionNotifyEventToClient(ClientPtr client,
Time time,
Window requestor,
Atom selection,
Atom target,
Atom property)
{
xEvent x = {0};
x.u.u.type = SelectionNotify;
x.u.selectionNotify.time = time;
x.u.selectionNotify.requestor = requestor;
x.u.selectionNotify.selection = selection;
x.u.selectionNotify.target = target;
x.u.selectionNotify.property = property;
#ifdef DEBUG
if (property == None)
fprintf (stderr, "%s: Denying request to client [%d].\n", __func__,
CLINDEX(client));
else
fprintf (stderr, "%s: Sending event to client [%d].\n", __func__,
CLINDEX(client));
#endif
return SendEventToClient(client, &x);
}
/*
* Check if target is a valid content type target sent by the real X
* server, like .e.g XA_STRING or UTF8_STRING. Other, non content type
* targets like "TARGETS" or "TIMESTAMP" will return false.
*/
Bool nxagentValidServerTargets(Atom target) Bool nxagentValidServerTargets(Atom target)
{ {
if (target == XA_STRING) if (target == XA_STRING)
...@@ -383,6 +479,13 @@ Bool nxagentValidServerTargets(Atom target) ...@@ -383,6 +479,13 @@ Bool nxagentValidServerTargets(Atom target)
#endif #endif
return False; return False;
} }
else if (target == serverTIMESTAMP)
{
#ifdef DEBUG
fprintf(stderr, "%s: special target [TIMESTAMP].\n", __func__);
#endif
return False;
}
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: invalid target [%u].\n", __func__, target); fprintf(stderr, "%s: invalid target [%u].\n", __func__, target);
...@@ -390,11 +493,28 @@ Bool nxagentValidServerTargets(Atom target) ...@@ -390,11 +493,28 @@ Bool nxagentValidServerTargets(Atom target)
return False; return False;
} }
void nxagentClearSelectionOwner(SelectionOwner *owner)
{
/* there's no owner on nxagent side anymore */
owner->client = NULL;
owner->window = None;
owner->lastTimeChanged = GetTimeInMillis();
/* FIXME: why is windowPtr not cleared in the function? */
}
void nxagentStoreSelectionOwner(SelectionOwner *owner, Selection *sel)
{
owner->client = sel->client;
owner->window = sel->window;
owner->windowPtr = sel->pWin;
owner->lastTimeChanged = GetTimeInMillis();
}
void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow) void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow)
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: Called with client [%p] window [%p].\n", __func__, fprintf(stderr, "%s: Called with client [%p] index [%d] window [%p] ([0x%x]).\n", __func__,
(void *) pClient, (void *) pWindow); (void *) pClient, CLINDEX(pClient), (void *) pWindow, WINDOWID(pWindow));
#endif #endif
nxagentPrintClipboardStat("before nxagentClearClipboard"); nxagentPrintClipboardStat("before nxagentClearClipboard");
...@@ -413,10 +533,9 @@ void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow) ...@@ -413,10 +533,9 @@ void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow)
(void *) pClient, (void *) pWindow); (void *) pClient, (void *) pWindow);
#endif #endif
lastSelectionOwner[i].client = NULL; /* FIXME: why is windowPtr not cleared in the function? */
lastSelectionOwner[i].window = None; nxagentClearSelectionOwner(&lastSelectionOwner[i]);
lastSelectionOwner[i].windowPtr = NULL; lastSelectionOwner[i].windowPtr = NULL;
lastSelectionOwner[i].lastTimeChanged = GetTimeInMillis();
lastClientWindowPtr = NULL; lastClientWindowPtr = NULL;
SetClientSelectionStage(None); SetClientSelectionStage(None);
...@@ -434,10 +553,35 @@ void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow) ...@@ -434,10 +553,35 @@ void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow)
nxagentPrintClipboardStat("after nxagentClearClipboard"); nxagentPrintClipboardStat("after nxagentClearClipboard");
} }
void nxagentClearSelection(XEvent *X) int nxagentFindLastSelectionOwnerIndex(Atom sel)
{ {
int i = 0; int i = 0;
while ((i < nxagentMaxSelections) &&
(lastSelectionOwner[i].selection != sel))
{
i++;
}
return i;
}
int nxagentFindCurrentSelectionIndex(Atom sel)
{
int i = 0;
while ((i < NumCurrentSelections) &&
(CurrentSelections[i].selection != sel))
{
i++;
}
return i;
}
/*
* This is called from Events.c dispatch loop on reception of a
* SelectionClear event. We receive this event if someone on the real
* X server claims the selection ownership.
*/
void nxagentClearSelection(XEvent *X)
{
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: SelectionClear event for selection [%lu].\n", __func__, X->xselectionclear.selection); fprintf(stderr, "%s: SelectionClear event for selection [%lu].\n", __func__, X->xselectionclear.selection);
#endif #endif
...@@ -450,34 +594,31 @@ void nxagentClearSelection(XEvent *X) ...@@ -450,34 +594,31 @@ void nxagentClearSelection(XEvent *X)
return; return;
} }
while ((i < nxagentMaxSelections) && int i = nxagentFindLastSelectionOwnerIndex(X->xselectionclear.selection);
(lastSelectionOwner[i].selection != X->xselectionclear.selection))
{
i++;
}
if (i < nxagentMaxSelections) if (i < nxagentMaxSelections)
{ {
if (lastSelectionOwner[i].client != NULL) if (lastSelectionOwner[i].client != NULL)
{ {
xEvent x; /* send a SelectionClear event to (our) previous owner */
memset(&x, 0, sizeof(xEvent)); xEvent x = {0};
x.u.u.type = SelectionClear; x.u.u.type = SelectionClear;
x.u.selectionClear.time = GetTimeInMillis(); x.u.selectionClear.time = GetTimeInMillis();
x.u.selectionClear.window = lastSelectionOwner[i].window; x.u.selectionClear.window = lastSelectionOwner[i].window;
x.u.selectionClear.atom = CurrentSelections[i].selection; x.u.selectionClear.atom = CurrentSelections[i].selection;
(void) TryClientEvents(lastSelectionOwner[i].client, &x, 1, SendEventToClient(lastSelectionOwner[i].client, &x);
NoEventMask, NoEventMask,
NullGrab);
} }
/*
* set the root window with the NullClient as selection owner. Our
* clients asking for the owner via XGetSelectionOwner() will get
* these for an answer
*/
CurrentSelections[i].window = screenInfo.screens[0]->root->drawable.id; CurrentSelections[i].window = screenInfo.screens[0]->root->drawable.id;
CurrentSelections[i].client = NullClient; CurrentSelections[i].client = NullClient;
lastSelectionOwner[i].client = NULL; nxagentClearSelectionOwner(&lastSelectionOwner[i]);
lastSelectionOwner[i].window = None;
lastSelectionOwner[i].lastTimeChanged = GetTimeInMillis();
} }
lastClientWindowPtr = NULL; lastClientWindowPtr = NULL;
...@@ -485,13 +626,63 @@ void nxagentClearSelection(XEvent *X) ...@@ -485,13 +626,63 @@ void nxagentClearSelection(XEvent *X)
nxagentPrintClipboardStat("after nxagentClearSelection"); nxagentPrintClipboardStat("after nxagentClearSelection");
} }
void nxagentRequestSelection(XEvent *X) /*
* Send a SelectionNotify event as reply to the RequestSelection
* event X. If success is True take the property from the event, else
* take None (which reports "failed/denied" to the requestor.
*/
void nxagentReplyRequestSelection(XEvent *X, Bool success)
{ {
int i = 0; XSelectionEvent eventSelection = {
XSelectionEvent eventSelection = {0}; .requestor = X->xselectionrequest.requestor,
.selection = X->xselectionrequest.selection,
.target = X->xselectionrequest.target,
.time = X->xselectionrequest.time,
.property = X->xselectionrequest.property
};
if (!success)
{
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: Got called.\n", __func__); fprintf(stderr, "%s: denying request\n", __func__);
#endif
eventSelection.property = None;
}
SendSelectionNotifyEventToServer(&eventSelection);
NXFlushDisplay(nxagentDisplay, NXFlushLink);
}
/*
* This is called from Events.c dispatch loop on reception of a
* SelectionRequest event, meaning a client of the real X server wants
* to have the selection content. The real X server knows the nxagent
* as selection owner. But in reality one of our windows is the owner,
* so we must pass the request on to the real owner.
*/
void nxagentRequestSelection(XEvent *X)
{
#ifdef DEBUG
{
char *strTarget = XGetAtomName(nxagentDisplay, X->xselectionrequest.target);
char *strSelection = XGetAtomName(nxagentDisplay, X->xselectionrequest.selection);
char *strProperty = XGetAtomName(nxagentDisplay, X->xselectionrequest.property);
fprintf(stderr, "%s: Received SelectionRequest from real server: selection [%ld][%s] " \
"target [%ld][%s] requestor [%s/0x%lx] destination [%ld][%s] lastServerRequestor [0x%x]\n",
__func__,
X->xselectionrequest.selection, validateString(strSelection),
X->xselectionrequest.target, validateString(strTarget),
DisplayString(nxagentDisplay), X->xselectionrequest.requestor,
X->xselectionrequest.property, validateString(strProperty),
lastServerRequestor);
SAFE_XFree(strTarget);
SAFE_XFree(strSelection);
SAFE_XFree(strProperty);
}
#endif #endif
nxagentPrintClipboardStat("before nxagentRequestSelection"); nxagentPrintClipboardStat("before nxagentRequestSelection");
...@@ -501,112 +692,122 @@ void nxagentRequestSelection(XEvent *X) ...@@ -501,112 +692,122 @@ void nxagentRequestSelection(XEvent *X)
return; return;
} }
/*
* check if this request needs special treatment by checking
* if any of the following is true:
* - this is a special request like TARGETS or TIMESTAMP
* - lastServerRequestor in non-NULL (= we are currenty in the transfer phase)
* - the selection in this request is none we own.
* In all cases we'll send back a SelectionNotify event with an
* appropriate answer
*/
if (!nxagentValidServerTargets(X->xselectionrequest.target) || if (!nxagentValidServerTargets(X->xselectionrequest.target) ||
(lastServerRequestor != None) || (lastServerRequestor != None) ||
((X->xselectionrequest.selection != lastSelectionOwner[nxagentPrimarySelection].selection) && ((X->xselectionrequest.selection != lastSelectionOwner[nxagentPrimarySelection].selection) &&
(X->xselectionrequest.selection != lastSelectionOwner[nxagentClipboardSelection].selection))) (X->xselectionrequest.selection != lastSelectionOwner[nxagentClipboardSelection].selection)))
{ {
/* if (X->xselectionrequest.target == serverTARGETS)
FIXME: Do we need this? {
/*
char *strTarget; * the selection request target is TARGETS. The requestor is
* asking for a list of supported data formats. Currently
strTarget = XGetAtomName(nxagentDisplay, X->xselectionrequest.target); * there's only one format we support: XA_STRING
*
fprintf(stderr, "SelectionRequest event aborting sele=[%s] target=[%s]\n", * The selection does not matter here, we will return this for
validateString(NameForAtom(X->xselectionrequest.selection)), * PRIMARY and CLIPBOARD.
validateString(NameForAtom(X->xselectionrequest.target))); *
* FIXME: shouldn't we support UTF8_STRING, too?
* FIXME: I am wondering if we should align this with
* nxagentConvertSelection, where we report more formats.
* FIXME: the perfect solution should not just answer with
* XA_STRING but ask the real owner what format it supports. The
* should then be sent to the original requestor.
* FIXME: these must be external Atoms!
*/
fprintf(stderr, "SelectionRequest event aborting sele=[%s] ext target=[%s] Atom size is [%d]\n", Atom targets[] = {XA_STRING};
validateString(NameForAtom(X->xselectionrequest.selection)), strTarget, sizeof(Atom)); int numTargets = 1;
SAFE_XFree(strTarget); #ifdef DEBUG
*/ fprintf(stderr, "%s: available targets:\n", __func__);
memset(&eventSelection, 0, sizeof(XSelectionEvent)); for (int i = 0; i < numTargets; i++)
eventSelection.property = None; fprintf(stderr, "%s: %s\n", __func__, NameForAtom(targets[i]));
fprintf(stderr, "\n");
#endif
if (X->xselectionrequest.target == serverTARGETS) /*
{ * pass on the requested list by setting the property provided
Atom xa_STRING = XA_STRING; * by the requestor accordingly.
XChangeProperty (nxagentDisplay, */
XChangeProperty(nxagentDisplay,
X->xselectionrequest.requestor, X->xselectionrequest.requestor,
X->xselectionrequest.property, X->xselectionrequest.property,
XInternAtom(nxagentDisplay, "ATOM", 0), XInternAtom(nxagentDisplay, "ATOM", 0),
sizeof(Atom)*8, sizeof(Atom)*8,
PropModeReplace, PropModeReplace,
(unsigned char*)&xa_STRING, (unsigned char*)&targets,
1); numTargets);
eventSelection.property = X->xselectionrequest.property;
nxagentReplyRequestSelection(X, True);
} }
else if (X->xselectionrequest.target == nxagentTimestampAtom) else if (X->xselectionrequest.target == serverTIMESTAMP)
{ {
while ((i < NumCurrentSelections) && /*
lastSelectionOwner[i].selection != X->xselectionrequest.selection) i++; * Section 2.6.2 of the ICCCM states:
* TIMESTAMP - To avoid some race conditions, it is important
* that requestors be able to discover the timestamp the owner
* used to acquire ownership. Until and unless the protocol is
* changed so that a GetSelectionOwner request returns the
* timestamp used to acquire ownership, selection owners must
* support conversion to TIMESTAMP, returning the timestamp they
* used to obtain the selection.
*
* FIXME: ensure we are reporting an _external_ timestamp
*/
if (i < NumCurrentSelections) int i = nxagentFindLastSelectionOwnerIndex(X->xselectionrequest.selection);
if (i < nxagentMaxSelections)
{ {
XChangeProperty(nxagentDisplay, XChangeProperty(nxagentDisplay,
X->xselectionrequest.requestor, X->xselectionrequest.requestor,
X->xselectionrequest.property, X->xselectionrequest.property,
X->xselectionrequest.target, XA_INTEGER,
32, 32,
PropModeReplace, PropModeReplace,
(unsigned char *) &lastSelectionOwner[i].lastTimeChanged, (unsigned char *) &lastSelectionOwner[i].lastTimeChanged,
1); 1);
eventSelection.property = X->xselectionrequest.property; nxagentReplyRequestSelection(X, True);
}
} }
eventSelection.type = SelectionNotify;
eventSelection.send_event = True;
eventSelection.display = nxagentDisplay;
eventSelection.requestor = X->xselectionrequest.requestor;
eventSelection.selection = X->xselectionrequest.selection;
eventSelection.target = X->xselectionrequest.target;
eventSelection.time = X->xselectionrequest.time;
#ifdef DEBUG
int result =
#endif
XSendEvent(nxagentDisplay,
eventSelection.requestor,
False,
0L,
(XEvent *) &eventSelection);
#ifdef DEBUG
fprintf(stderr, "%s: XSendEvent() returned [%s]\n", __func__, GetXErrorString(result));
if (result == BadValue || result == BadWindow)
{
fprintf(stderr, "%s: WARNING! XSendEvent failed.\n", __func__);
} }
else else
{ {
fprintf(stderr, "%s: XSendEvent sent to window [0x%lx].\n", __func__, /* deny the request */
eventSelection.requestor); nxagentReplyRequestSelection(X, False);
} }
#endif
return; return;
} }
/* /*
* This is necessary in nxagentGetClipboardWindow. * reaching this means the request is neither a special request nor
* invalid. We can process it now.
*/ */
/*
* This is required for nxagentGetClipboardWindow.
*/
nxagentLastRequestedSelection = X->xselectionrequest.selection; nxagentLastRequestedSelection = X->xselectionrequest.selection;
/* FIXME: shouldn't we reset i to 0 here first? */ /* find the index of the requested selection */
while ((i < nxagentMaxSelections) && int i = nxagentFindLastSelectionOwnerIndex(X->xselectionrequest.selection);
(lastSelectionOwner[i].selection != X->xselectionrequest.selection))
{
i++;
}
if (i < nxagentMaxSelections) if (i < nxagentMaxSelections)
{ {
if ((lastClientWindowPtr != NULL) && (lastSelectionOwner[i].client != NULL)) if ((lastClientWindowPtr != NULL) && (lastSelectionOwner[i].client != NULL))
{ {
/*
* Request the real X server to transfer the selection content
* to the NX_CUT_BUFFER_CLIENT property of the serverWindow.
* FIXME: document how we can end up here
*/
XConvertSelection(nxagentDisplay, CurrentSelections[i].selection, XConvertSelection(nxagentDisplay, CurrentSelections[i].selection,
X->xselectionrequest.target, serverCutProperty, X->xselectionrequest.target, serverCutProperty,
serverWindow, lastClientTime); serverWindow, lastClientTime);
...@@ -617,141 +818,121 @@ FIXME: Do we need this? ...@@ -617,141 +818,121 @@ FIXME: Do we need this?
} }
else else
{ {
/*
* if one of our clients owns the selection we ask it to copy
* the selection to the clientCutProperty on nxagent's root
* window
*/
if (lastSelectionOwner[i].client != NULL && if (lastSelectionOwner[i].client != NULL &&
nxagentOption(Clipboard) != ClipboardClient) nxagentOption(Clipboard) != ClipboardClient)
{ {
xEvent x; /*
* store who on the real X server requested the data and how
* and where it wants to have it
*/
lastServerProperty = X->xselectionrequest.property; lastServerProperty = X->xselectionrequest.property;
lastServerRequestor = X->xselectionrequest.requestor; lastServerRequestor = X->xselectionrequest.requestor;
lastServerTarget = X->xselectionrequest.target; lastServerTarget = X->xselectionrequest.target;
lastServerTime = X->xselectionrequest.time;
/* by dimbor */ /* by dimbor */
if (lastServerTarget != XA_STRING) if (lastServerTarget != XA_STRING)
lastServerTarget = serverUTF8_STRING; lastServerTarget = serverUTF8_STRING;
lastServerTime = X->xselectionrequest.time; /* prepare the request (like XConvertSelection, but internally) */
xEvent x = {0};
memset(&x, 0, sizeof(xEvent));
x.u.u.type = SelectionRequest; x.u.u.type = SelectionRequest;
x.u.selectionRequest.time = GetTimeInMillis(); x.u.selectionRequest.time = GetTimeInMillis();
x.u.selectionRequest.owner = lastSelectionOwner[i].window; x.u.selectionRequest.owner = lastSelectionOwner[i].window;
x.u.selectionRequest.selection = CurrentSelections[i].selection;
x.u.selectionRequest.property = clientCutProperty;
x.u.selectionRequest.requestor = screenInfo.screens[0]->root->drawable.id; /* Fictitious window.*/
/* /*
* Fictitious window. * Don't send the same window, some programs are clever and
*/ * verify cut and paste operations inside the same window and
* don't Notify at all.
x.u.selectionRequest.requestor = screenInfo.screens[0]->root->drawable.id;
/*
* Don't send the same window, some programs are
* clever and verify cut and paste operations
* inside the same window and don't Notify at all.
* *
* x.u.selectionRequest.requestor = lastSelectionOwnerWindow; * x.u.selectionRequest.requestor = lastSelectionOwnerWindow;
*/ */
x.u.selectionRequest.selection = CurrentSelections[i].selection;
/* by dimbor (idea from zahvatov) */ /* by dimbor (idea from zahvatov) */
if (X->xselectionrequest.target != XA_STRING) if (X->xselectionrequest.target != XA_STRING)
x.u.selectionRequest.target = clientUTF8_STRING; x.u.selectionRequest.target = clientUTF8_STRING;
else else
x.u.selectionRequest.target = XA_STRING; x.u.selectionRequest.target = XA_STRING;
x.u.selectionRequest.property = clientCutProperty; SendEventToClient(lastSelectionOwner[i].client, &x);
(void) TryClientEvents(lastSelectionOwner[i].client, &x, 1,
NoEventMask, NoEventMask /* CantBeFiltered */,
NullGrab);
#ifdef DEBUG
fprintf(stderr, "%s: Executed TryClientEvents with clientCutProperty.\n", __func__);
#endif
}
else
{
/*
* Probably we must send a Notify
* to requestor with property None.
*/
eventSelection.type = SelectionNotify;
eventSelection.send_event = True;
eventSelection.display = nxagentDisplay;
eventSelection.requestor = X->xselectionrequest.requestor;
eventSelection.selection = X->xselectionrequest.selection;
eventSelection.target = X->xselectionrequest.target;
eventSelection.property = None;
eventSelection.time = X->xselectionrequest.time;
#ifdef DEBUG #ifdef DEBUG
int result = fprintf(stderr, "%s: sent SelectionRequest event to client [%d] property [%d][%s]" \
"target [%d][%s] requestor [0x%x].\n", __func__,
CLINDEX(lastSelectionOwner[i].client),
x.u.selectionRequest.property, NameForAtom(x.u.selectionRequest.property),
x.u.selectionRequest.target, NameForAtom(x.u.selectionRequest.target),
x.u.selectionRequest.requestor);
#endif #endif
XSendEvent(nxagentDisplay,
eventSelection.requestor,
False,
0L,
(XEvent *) &eventSelection);
#ifdef DEBUG
fprintf(stderr, "%s: XSendEvent() returned [%s]\n", __func__, GetXErrorString(result));
if (result == BadValue || result == BadWindow)
{
fprintf(stderr, "%s: WARNING! XSendEvent failed.\n", __func__);
} }
else else
{ {
fprintf(stderr, "%s: XSendEvent with property None sent to window [0x%lx].\n", __func__, /* deny the request */
eventSelection.requestor); nxagentReplyRequestSelection(X, False);
}
#endif
} }
} }
} }
nxagentPrintClipboardStat("after nxagentRequestSelection"); nxagentPrintClipboardStat("after nxagentRequestSelection");
} }
void nxagentSendSelectionNotify(Atom property) /*
* end current selection transfer by sending a notification to the
* client and resetting the corresponding variables and the state
* machine. If success is False send a None reply, meaning "request
* denied/failed"
* Use SELECTION_SUCCESS and SELECTION_FAULT macros for success.
*/
static void endTransfer(Bool success)
{ {
xEvent x; if (lastClientClientPtr == NULL)
{
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: Sending event to client [%d].\n", __func__, fprintf(stderr, "%s: lastClientClientPtr is NULL - doing nothing.\n", __func__);
lastClientClientPtr -> index);
#endif #endif
return;
}
memset(&x, 0, sizeof(xEvent)); #ifdef DEBUG
x.u.u.type = SelectionNotify; if (success == SELECTION_SUCCESS)
fprintf(stderr, "%s: sending notification to client [%d], property [%d][%s]\n", __func__,
x.u.selectionNotify.time = lastClientTime; CLINDEX(lastClientClientPtr), lastClientProperty, NameForAtom(lastClientProperty));
x.u.selectionNotify.requestor = lastClientRequestor; else
x.u.selectionNotify.selection = lastClientSelection; fprintf(stderr, "%s: sending negative notification to client [%d]\n", __func__,
x.u.selectionNotify.target = lastClientTarget; CLINDEX(lastClientClientPtr));
#endif
x.u.selectionNotify.property = property;
TryClientEvents(lastClientClientPtr, &x, 1, NoEventMask, SendSelectionNotifyEventToClient(lastClientClientPtr,
NoEventMask , NullGrab); lastClientTime,
lastClientRequestor,
lastClientSelection,
lastClientTarget,
success == SELECTION_SUCCESS ? lastClientProperty : None);
return; /*
* Enable further requests from clients.
*/
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
} }
void nxagentTransferSelection(int resource) void nxagentTransferSelection(int resource)
{ {
int result;
if (lastClientClientPtr -> index != resource) if (lastClientClientPtr -> index != resource)
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: WARNING! Inconsistent resource [%d] with current client [%d].\n", __func__, fprintf (stderr, "%s: WARNING! Inconsistent resource [%d] with current client [%d].\n", __func__,
resource, lastClientClientPtr -> index); resource, CLINDEX(lastClientClientPtr));
#endif #endif
nxagentSendSelectionNotify(None); endTransfer(SELECTION_FAULT);
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
return; return;
} }
...@@ -760,6 +941,8 @@ void nxagentTransferSelection(int resource) ...@@ -760,6 +941,8 @@ void nxagentTransferSelection(int resource)
{ {
case SelectionStageQuerySize: case SelectionStageQuerySize:
{ {
int result;
PrintClientSelectionStage(); PrintClientSelectionStage();
/* /*
* Don't get data yet, just get size. We skip * Don't get data yet, just get size. We skip
...@@ -793,23 +976,24 @@ void nxagentTransferSelection(int resource) ...@@ -793,23 +976,24 @@ void nxagentTransferSelection(int resource)
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: Aborting selection notify procedure for client [%d].\n", __func__, fprintf (stderr, "%s: Aborting selection notify procedure for client [%d].\n", __func__,
lastClientClientPtr -> index); CLINDEX(lastClientClientPtr));
#endif #endif
nxagentSendSelectionNotify(None); endTransfer(SELECTION_FAULT);
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
return; return;
} }
SetClientSelectionStage(WaitSize); SetClientSelectionStage(WaitSize);
NXFlushDisplay(nxagentDisplay, NXFlushLink);
break; break;
} }
case SelectionStageQueryData: case SelectionStageQueryData:
{ {
int result;
PrintClientSelectionStage(); PrintClientSelectionStage();
/* /*
...@@ -846,26 +1030,27 @@ void nxagentTransferSelection(int resource) ...@@ -846,26 +1030,27 @@ void nxagentTransferSelection(int resource)
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: Aborting selection notify procedure for client [%d].\n", __func__, fprintf (stderr, "%s: Aborting selection notify procedure for client [%d].\n", __func__,
lastClientClientPtr -> index); CLINDEX(lastClientClientPtr));
#endif #endif
nxagentSendSelectionNotify(None); endTransfer(SELECTION_FAULT);
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
return; return;
} }
SetClientSelectionStage(WaitData); SetClientSelectionStage(WaitData);
/* we've seen situations where you had to move the mouse or press a
key to let the transfer complete. Flushing here fixed it */
NXFlushDisplay(nxagentDisplay, NXFlushLink);
break; break;
} }
default: default:
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: WARNING! Inconsistent state [%s] for client [%d].\n", __func__, fprintf (stderr, "%s: WARNING! Inconsistent state [%s] for client [%d].\n", __func__,
GetClientSelectionStageString(lastClientStage), lastClientClientPtr -> index); GetClientSelectionStageString(lastClientStage), CLINDEX(lastClientClientPtr));
#endif #endif
break; break;
...@@ -873,6 +1058,13 @@ void nxagentTransferSelection(int resource) ...@@ -873,6 +1058,13 @@ void nxagentTransferSelection(int resource)
} }
} }
/*
Called from Events.c/nxagentHandlePropertyNotify
This event is generated after XChangeProperty(), XDeleteProperty() or
XGetWindowProperty(delete=True)
*/
void nxagentCollectPropertyEvent(int resource) void nxagentCollectPropertyEvent(int resource)
{ {
Atom atomReturnType; Atom atomReturnType;
...@@ -883,9 +1075,8 @@ void nxagentCollectPropertyEvent(int resource) ...@@ -883,9 +1075,8 @@ void nxagentCollectPropertyEvent(int resource)
int result; int result;
/* /*
* We have received the notification so * We have received the notification so we can safely retrieve data
* we can safely retrieve data from the * from the client structure.
* client structure.
*/ */
result = NXGetCollectedProperty(nxagentDisplay, result = NXGetCollectedProperty(nxagentDisplay,
...@@ -901,37 +1092,21 @@ void nxagentCollectPropertyEvent(int resource) ...@@ -901,37 +1092,21 @@ void nxagentCollectPropertyEvent(int resource)
if (result == 0) if (result == 0)
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: Failed to get reply data for client [%d].\n", __func__, fprintf (stderr, "%s: Failed to get reply data.\n", __func__);
lastClientClientPtr -> index);
#endif #endif
nxagentSendSelectionNotify(None); endTransfer(SELECTION_FAULT);
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
SAFE_XFree(pszReturnData);
return;
} }
else if (resultFormat != 8 && resultFormat != 16 && resultFormat != 32)
if (resultFormat != 8 && resultFormat != 16 && resultFormat != 32)
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: WARNING! Invalid property value.\n", __func__); fprintf (stderr, "%s: WARNING! Invalid property format.\n", __func__);
#endif #endif
if (lastClientClientPtr != NULL) endTransfer(SELECTION_FAULT);
{
nxagentSendSelectionNotify(None);
}
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
SAFE_XFree(pszReturnData);
return;
} }
else
{
switch (lastClientStage) switch (lastClientStage)
{ {
case SelectionStageWaitSize: case SelectionStageWaitSize:
...@@ -939,25 +1114,19 @@ void nxagentCollectPropertyEvent(int resource) ...@@ -939,25 +1114,19 @@ void nxagentCollectPropertyEvent(int resource)
PrintClientSelectionStage(); PrintClientSelectionStage();
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: Got size notify event for client [%d].\n", __func__, fprintf (stderr, "%s: Got size notify event for client [%d].\n", __func__,
lastClientClientPtr -> index); CLINDEX(lastClientClientPtr));
#endif #endif
if (ulReturnBytesLeft == 0) if (ulReturnBytesLeft == 0)
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: Aborting selection notify procedure for client [%d].\n", __func__, fprintf (stderr, "%s: Aborting selection notify procedure.\n", __func__);
lastClientClientPtr -> index);
#endif #endif
nxagentSendSelectionNotify(None); endTransfer(SELECTION_FAULT);
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
SAFE_XFree(pszReturnData);
return;
} }
else
{
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: Got property size from remote server.\n", __func__); fprintf(stderr, "%s: Got property size from remote server.\n", __func__);
#endif #endif
...@@ -965,12 +1134,11 @@ void nxagentCollectPropertyEvent(int resource) ...@@ -965,12 +1134,11 @@ void nxagentCollectPropertyEvent(int resource)
/* /*
* Request the selection data now. * Request the selection data now.
*/ */
lastClientPropertySize = ulReturnBytesLeft; lastClientPropertySize = ulReturnBytesLeft;
SetClientSelectionStage(QueryData); SetClientSelectionStage(QueryData);
nxagentTransferSelection(resource); nxagentTransferSelection(resource);
}
break; break;
} }
case SelectionStageWaitData: case SelectionStageWaitData:
...@@ -978,25 +1146,19 @@ void nxagentCollectPropertyEvent(int resource) ...@@ -978,25 +1146,19 @@ void nxagentCollectPropertyEvent(int resource)
PrintClientSelectionStage(); PrintClientSelectionStage();
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: Got data notify event for client [%d].\n", __func__, fprintf (stderr, "%s: Got data notify event for client [%d].\n", __func__,
lastClientClientPtr -> index); CLINDEX(lastClientClientPtr));
#endif #endif
if (ulReturnBytesLeft != 0) if (ulReturnBytesLeft != 0)
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: Aborting selection notify procedure for client [%d].\n", __func__, fprintf (stderr, "%s: Aborting selection notify procedure.\n", __func__);
lastClientClientPtr -> index);
#endif #endif
nxagentSendSelectionNotify(None); endTransfer(SELECTION_FAULT);
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
SAFE_XFree(pszReturnData);
return;
} }
else
{
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: Got property content from remote server.\n", __func__); fprintf(stderr, "%s: Got property content from remote server.\n", __func__);
#endif #endif
...@@ -1008,61 +1170,73 @@ void nxagentCollectPropertyEvent(int resource) ...@@ -1008,61 +1170,73 @@ void nxagentCollectPropertyEvent(int resource)
ulReturnItems, pszReturnData, 1); ulReturnItems, pszReturnData, 1);
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: Selection property [%s] changed to [%s]\n", __func__, fprintf(stderr, "%s: Selection property [%d][%s] changed to [\"%*.*s\"...]\n", __func__,
validateString(NameForAtom(lastClientProperty)), pszReturnData); lastClientProperty, validateString(NameForAtom(lastClientProperty)),
(int)(min(20, ulReturnItems * resultFormat / 8)),
(int)(min(20, ulReturnItems * resultFormat / 8)),
pszReturnData);
#endif #endif
nxagentSendSelectionNotify(lastClientProperty); endTransfer(SELECTION_SUCCESS);
}
/*
* Enable further requests from clients.
*/
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
break; break;
} }
default: default:
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf (stderr, "%s: WARNING! Inconsistent state [%s] for client [%d].\n", __func__, fprintf (stderr, "%s: WARNING! Inconsistent state [%s] for client [%d].\n", __func__,
GetClientSelectionStageString(lastClientStage), lastClientClientPtr -> index); GetClientSelectionStageString(lastClientStage), CLINDEX(lastClientClientPtr));
#endif #endif
break; break;
} }
} }
}
SAFE_XFree(pszReturnData); SAFE_XFree(pszReturnData);
} }
void nxagentNotifySelection(XEvent *X) /*
* This is _only_ called from Events.c dispatch loop on reception of a
* SelectionNotify event from the real X server. These events are
* sent out by nxagent itself!
*/
void nxagentHandleSelectionNotifyFromXServer(XEvent *X)
{ {
XSelectionEvent eventSelection;
#ifdef DEBUG
fprintf(stderr, "%s: Got called.\n", __func__);
#endif
if (agentClipboardStatus != 1) if (agentClipboardStatus != 1)
{ {
return; return;
} }
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: SelectionNotify event.\n", __func__); {
XSelectionEvent * e = (XSelectionEvent *)X;
char * s = XGetAtomName(nxagentDisplay, e->property);
char * t = XGetAtomName(nxagentDisplay, e->target);
fprintf(stderr, "%s: SelectionNotify event from real X server, property "\
"[%ld][%s] requestor [0x%lx] target [%ld][%s] time [%ld] send_event [%d].\n",
__func__, e->property, validateString(s), e->requestor, e->target,
validateString(t), e->time, e->send_event);
SAFE_XFree(s);
SAFE_XFree(t);
}
#endif #endif
PrintClientSelectionStage(); PrintClientSelectionStage();
if (lastClientWindowPtr != NULL) if (lastClientWindowPtr != NULL)
{ {
/*
* We reach here after a paste inside the nxagent, triggered by
* the XConvertSelection call in nxagentConvertSelection(). This
* means that data we need has been transferred to the
* serverCutProperty of the serverWindow (our window on the real X
* server). We now need to transfer it to the original requestor,
* which is stored in the lastClient* variables.
*/
if ((lastClientStage == SelectionStageNone) && (X->xselection.property == serverCutProperty)) if ((lastClientStage == SelectionStageNone) && (X->xselection.property == serverCutProperty))
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: Starting selection transferral for client [%d].\n", __func__, fprintf(stderr, "%s: Starting selection transferral for client [%d].\n", __func__,
lastClientClientPtr -> index); CLINDEX(lastClientClientPtr));
#endif #endif
/* /*
...@@ -1085,31 +1259,21 @@ void nxagentNotifySelection(XEvent *X) ...@@ -1085,31 +1259,21 @@ void nxagentNotifySelection(XEvent *X)
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: WARNING! Resetting selection transferral for client [%d].\n", __func__, fprintf(stderr, "%s: WARNING! Resetting selection transferral for client [%d].\n", __func__,
lastClientClientPtr -> index); CLINDEX(lastClientClientPtr));
#endif #endif
nxagentSendSelectionNotify(None); endTransfer(SELECTION_FAULT);
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
} }
return;
} }
else else
{ {
int i = 0; int i = nxagentFindLastSelectionOwnerIndex(X->xselection.selection);
while ((i < nxagentMaxSelections) && (lastSelectionOwner[i].selection != X->xselection.selection))
{
i++;
}
if (i < nxagentMaxSelections) if (i < nxagentMaxSelections)
{ {
/* if the last owner was an internal one */
if ((lastSelectionOwner[i].client != NULL) && if ((lastSelectionOwner[i].client != NULL) &&
(lastSelectionOwner[i].windowPtr != NULL) && (lastSelectionOwner[i].windowPtr != NULL) &&
(X->xselection.property == clientCutProperty)) (X->xselection.property == serverClientCutProperty))
{ {
Atom atomReturnType; Atom atomReturnType;
int resultFormat; int resultFormat;
...@@ -1117,38 +1281,44 @@ void nxagentNotifySelection(XEvent *X) ...@@ -1117,38 +1281,44 @@ void nxagentNotifySelection(XEvent *X)
unsigned long ulReturnBytesLeft; unsigned long ulReturnBytesLeft;
unsigned char *pszReturnData = NULL; unsigned char *pszReturnData = NULL;
/* first get size values ... */
int result = GetWindowProperty(lastSelectionOwner[i].windowPtr, clientCutProperty, 0, 0, False, int result = GetWindowProperty(lastSelectionOwner[i].windowPtr, clientCutProperty, 0, 0, False,
AnyPropertyType, &atomReturnType, &resultFormat, AnyPropertyType, &atomReturnType, &resultFormat,
&ulReturnItems, &ulReturnBytesLeft, &pszReturnData); &ulReturnItems, &ulReturnBytesLeft, &pszReturnData);
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: GetWindowProperty() returned [%s]\n", __func__, GetXErrorString(result)); fprintf(stderr, "%s: GetWindowProperty() window [0x%x] property [%d] returned [%s]\n", __func__,
lastSelectionOwner[i].window, clientCutProperty, GetXErrorString(result));
#endif #endif
if (result == BadAlloc || result == BadAtom || if (result == BadAlloc || result == BadAtom ||
result == BadWindow || result == BadValue) result == BadWindow || result == BadValue)
{ {
fprintf (stderr, "Client GetProperty failed. Error = %s", GetXErrorString(result));
lastServerProperty = None; lastServerProperty = None;
} }
else else
{ {
/* ... then use the size values for the actual request */
result = GetWindowProperty(lastSelectionOwner[i].windowPtr, clientCutProperty, 0, result = GetWindowProperty(lastSelectionOwner[i].windowPtr, clientCutProperty, 0,
ulReturnBytesLeft, False, AnyPropertyType, &atomReturnType, ulReturnBytesLeft, False, AnyPropertyType, &atomReturnType,
&resultFormat, &ulReturnItems, &ulReturnBytesLeft, &resultFormat, &ulReturnItems, &ulReturnBytesLeft,
&pszReturnData); &pszReturnData);
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: GetWindowProperty() returned [%s]\n", __func__, GetXErrorString(result)); fprintf(stderr, "%s: GetWindowProperty() window [0x%x] property [%d] returned [%s]\n", __func__,
lastSelectionOwner[i].window, clientCutProperty, GetXErrorString(result));
#endif #endif
if (result == BadAlloc || result == BadAtom || if (result == BadAlloc || result == BadAtom ||
result == BadWindow || result == BadValue) result == BadWindow || result == BadValue)
{ {
fprintf (stderr, "SelectionNotify - XChangeProperty failed. Error = %s\n", GetXErrorString(result));
lastServerProperty = None; lastServerProperty = None;
} }
else else
{ {
result = XChangeProperty(nxagentDisplay, /* Fill the property on the initial requestor with the requested data */
/* The XChangeProperty source code reveals it will always
return 1, no matter what, so no need to check the result */
/* FIXME: better use the format returned by above request */
XChangeProperty(nxagentDisplay,
lastServerRequestor, lastServerRequestor,
lastServerProperty, lastServerProperty,
lastServerTarget, lastServerTarget,
...@@ -1156,55 +1326,52 @@ void nxagentNotifySelection(XEvent *X) ...@@ -1156,55 +1326,52 @@ void nxagentNotifySelection(XEvent *X)
PropModeReplace, PropModeReplace,
pszReturnData, pszReturnData,
ulReturnItems); ulReturnItems);
}
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: XChangeProperty() returned [%s]\n", __func__, GetXErrorString(result)); {
char *s = XGetAtomName(nxagentDisplay, lastServerProperty);
fprintf(stderr, "%s: XChangeProperty sent to window [0x%x] for property [%d][%s] value [\"%*.*s\"...]\n",
__func__,
lastServerRequestor,
lastServerProperty,
s,
(int)(min(20, ulReturnItems * 8 / 8)),
(int)(min(20, ulReturnItems * 8 / 8)),
pszReturnData);
SAFE_XFree(s);
}
#endif #endif
}
/* FIXME: free it or not? */
/* /*
* SAFE_XFree(pszReturnData); * SAFE_XFree(pszReturnData);
*/ */
} }
memset(&eventSelection, 0, sizeof(XSelectionEvent));
eventSelection.type = SelectionNotify;
eventSelection.send_event = True;
eventSelection.display = nxagentDisplay;
eventSelection.requestor = lastServerRequestor;
eventSelection.selection = X->xselection.selection;
/* /*
* eventSelection.target = X->xselection.target; * inform the initial requestor that the requested data has
* arrived in the desired property. If we have been unable to
* get the data from the owner XChangeProperty will not have
* been called and lastServerProperty will be None which
* effectively will send a "Request denied" to the initial
* requestor.
*/ */
XSelectionEvent eventSelection = {
eventSelection.target = lastServerTarget; .requestor = lastServerRequestor,
eventSelection.property = lastServerProperty; .selection = X->xselection.selection,
eventSelection.time = lastServerTime; /* .target = X->xselection.target, */
.target = lastServerTarget,
/* .property = lastServerProperty,
* eventSelection.time = CurrentTime; .time = lastServerTime,
* eventSelection.time = lastServerTime; /* .time = CurrentTime */
*/ };
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: Sending event to requestor [%p].\n", __func__, (void *)eventSelection.requestor); fprintf(stderr, "%s: Sending SelectionNotify event to requestor [%p].\n", __func__,
(void *)eventSelection.requestor);
#endif #endif
result = XSendEvent(nxagentDisplay, SendSelectionNotifyEventToServer(&eventSelection);
eventSelection.requestor,
False,
0L,
(XEvent *) &eventSelection);
#ifdef DEBUG
fprintf(stderr, "%s: XSendEvent() returned [%s]\n", __func__, GetXErrorString(result));
#endif
if (result == BadValue || result == BadWindow)
{
fprintf (stderr, "SelectionRequest - XSendEvent failed\n");
}
lastServerRequestor = None; /* allow further request */ lastServerRequestor = None; /* allow further request */
} }
...@@ -1213,21 +1380,24 @@ void nxagentNotifySelection(XEvent *X) ...@@ -1213,21 +1380,24 @@ void nxagentNotifySelection(XEvent *X)
} }
/* /*
* Acquire selection so we don't get selection * Let nxagent's serverWindow acquire the selection. All requests from
* requests from real X clients. * the real X server (or its clients) will be sent to this window. The
* real X server never communicates with our windows directly.
*/ */
void nxagentResetSelectionOwner(void) void nxagentResetSelectionOwner(void)
{ {
int i;
if (lastServerRequestor != None) if (lastServerRequestor != None)
{ {
#ifdef TEST /*
* we are in the process of communicating back and forth between
* real X server and nxagent's clients - let's not disturb.
*/
#if defined(TEST) || defined(DEBUG)
fprintf(stderr, "%s: WARNING! Requestor window [0x%x] already found.\n", __func__, fprintf(stderr, "%s: WARNING! Requestor window [0x%x] already found.\n", __func__,
lastServerRequestor); lastServerRequestor);
#endif #endif
/* FIXME: maybe we should put back the event that lead us here. */
return; return;
} }
...@@ -1235,39 +1405,44 @@ void nxagentResetSelectionOwner(void) ...@@ -1235,39 +1405,44 @@ void nxagentResetSelectionOwner(void)
* Only for PRIMARY and CLIPBOARD selections. * Only for PRIMARY and CLIPBOARD selections.
*/ */
for (i = 0; i < nxagentMaxSelections; i++) for (int i = 0; i < nxagentMaxSelections; i++)
{ {
XSetSelectionOwner(nxagentDisplay, lastSelectionOwner[i].selection, serverWindow, CurrentTime); XSetSelectionOwner(nxagentDisplay, lastSelectionOwner[i].selection, serverWindow, CurrentTime);
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: Reset clipboard state.\n", __func__); fprintf(stderr, "%s: Reset selection state for selection [%d].\n", __func__, i);
#endif #endif
lastSelectionOwner[i].client = NULL; nxagentClearSelectionOwner(&lastSelectionOwner[i]);
lastSelectionOwner[i].window = None;
lastSelectionOwner[i].windowPtr = NULL; lastSelectionOwner[i].windowPtr = NULL;
lastSelectionOwner[i].lastTimeChanged = GetTimeInMillis();
} }
lastClientWindowPtr = NULL; lastClientWindowPtr = NULL;
SetClientSelectionStage(None); SetClientSelectionStage(None);
/* Hmm, this is already None when reaching this */
lastServerRequestor = None; lastServerRequestor = None;
return;
} }
#ifdef NXAGENT_CLIPBOARD #ifdef NXAGENT_CLIPBOARD
/*
* The callback is called from dix. This is the normal operation
* mode. The callback is also called when nxagent gets XFixes events
* from the real X server. In that case the Trap is set and the
* callback will do nothing.
*/
void nxagentSetSelectionCallback(CallbackListPtr *callbacks, void *data, void nxagentSetSelectionCallback(CallbackListPtr *callbacks, void *data,
void *args) void *args)
{ {
/* /*
* Only act if the Trap is unset. The trap indicates that we are * Only act if the trap is unset. The trap indicates that we are
* triggered by a clipboard event originating from the real X * triggered by an XFixes clipboard event originating from the real
* server. In that case we do not want to propagate back changes to * X server. In that case we do not want to propagate back changes
* the real X server, because it already knows about them and we * to the real X server, because it already knows about them and we
* would end up in an infinite loop of events. If there was a better * would end up in an infinite loop of events. If there was a better
* way to identify that situation during Callback processing we * way to identify that situation during callback processing we
* could get rid of the Trap... * could get rid of the Trap...
*/ */
if (nxagentExternalClipboardEventTrap != 0) if (nxagentExternalClipboardEventTrap != 0)
...@@ -1290,7 +1465,7 @@ void nxagentSetSelectionCallback(CallbackListPtr *callbacks, void *data, ...@@ -1290,7 +1465,7 @@ void nxagentSetSelectionCallback(CallbackListPtr *callbacks, void *data,
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: called with SelectionCallbackKind SelectionSetOwner\n", __func__); fprintf(stderr, "%s: called with SelectionCallbackKind SelectionSetOwner\n", __func__);
fprintf(stderr, "%s: pCurSel->pWin [0x%x]\n", __func__, pCurSel->pWin ? pCurSel->pWin->drawable.id : NULL); fprintf(stderr, "%s: pCurSel->pWin [0x%x]\n", __func__, WINDOWID(pCurSel->pWin));
fprintf(stderr, "%s: pCurSel->selection [%s]\n", __func__, NameForAtom(pCurSel->selection)); fprintf(stderr, "%s: pCurSel->selection [%s]\n", __func__, NameForAtom(pCurSel->selection));
#endif #endif
...@@ -1326,12 +1501,12 @@ void nxagentSetSelectionCallback(CallbackListPtr *callbacks, void *data, ...@@ -1326,12 +1501,12 @@ void nxagentSetSelectionCallback(CallbackListPtr *callbacks, void *data,
} }
#endif #endif
/*
* This is called from the nxagentSetSelectionCallback, so it is using
* internal Atoms
*/
void nxagentSetSelectionOwner(Selection *pSelection) void nxagentSetSelectionOwner(Selection *pSelection)
{ {
#ifdef DEBUG
fprintf(stderr, "%s: Got called.\n", __func__);
#endif
if (agentClipboardStatus != 1) if (agentClipboardStatus != 1)
{ {
return; return;
...@@ -1342,10 +1517,14 @@ void nxagentSetSelectionOwner(Selection *pSelection) ...@@ -1342,10 +1517,14 @@ void nxagentSetSelectionOwner(Selection *pSelection)
serverWindow); serverWindow);
#endif #endif
#ifdef TEST #if defined(TEST) || defined(DEBUG)
if (lastServerRequestor != None) if (lastServerRequestor != None)
{ {
fprintf (stderr, "%s: WARNING! Requestor window [0x%x] already found.\n", __func__, /*
* we are in the process of communicating back and forth between
* real X server and nxagent's clients - let's not disturb
*/
fprintf (stderr, "%s: WARNING! Requestor window [0x%x] already set.\n", __func__,
lastServerRequestor); lastServerRequestor);
} }
#endif #endif
...@@ -1356,28 +1535,37 @@ void nxagentSetSelectionOwner(Selection *pSelection) ...@@ -1356,28 +1535,37 @@ void nxagentSetSelectionOwner(Selection *pSelection)
for (int i = 0; i < nxagentMaxSelections; i++) for (int i = 0; i < nxagentMaxSelections; i++)
{ {
/* FIXME: using CurrentSelections with the index limited my MaxSelections looks wrong */
if (pSelection->selection == CurrentSelections[i].selection) if (pSelection->selection == CurrentSelections[i].selection)
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: lastSelectionOwner.client [0x%x] -> [0x%x]\n", __func__, lastSelectionOwner[i].client, pSelection->client); fprintf(stderr, "%s: lastSelectionOwner.client [%p] index [%d] -> [%p] index [%d]\n", __func__,
fprintf(stderr, "%s: lastSelectionOwner.window [0x%x] -> [0x%x]\n", __func__, lastSelectionOwner[i].window, pSelection->window); (void *)lastSelectionOwner[i].client,
fprintf(stderr, "%s: lastSelectionOwner.windowPtr [0x%x] -> [0x%x] [0x%x] (serverWindow: [0x%x])\n", __func__, lastSelectionOwner[i].windowPtr, pSelection->pWin, nxagentWindow(pSelection->pWin), serverWindow); CLINDEX(lastSelectionOwner[i].client),
fprintf(stderr, "%s: lastSelectionOwner.lastTimeChanged [%d]\n", __func__, lastSelectionOwner[i].lastTimeChanged); (void *)pSelection->client,
CLINDEX(pSelection->client));
fprintf(stderr, "%s: lastSelectionOwner.window [0x%x] -> [0x%x]\n", __func__,
lastSelectionOwner[i].window, pSelection->window);
fprintf(stderr, "%s: lastSelectionOwner.windowPtr [%p] -> [%p] [0x%x] (serverWindow: [0x%x])\n", __func__,
(void *)lastSelectionOwner[i].windowPtr, (void *)pSelection->pWin,
nxagentWindow(pSelection->pWin), serverWindow);
fprintf(stderr, "%s: lastSelectionOwner.lastTimeChanged [%d]\n", __func__,
lastSelectionOwner[i].lastTimeChanged);
#endif #endif
/* /*
* inform the real X server that our serverWindow is the * inform the real X server that our serverWindow is the
* clipboard owner. The real owner window (inside nxagent) is * clipboard owner.
* stored in lastSelectionOwner.window.
* lastSelectionOwner.windowPtr points to the struct that
* contains all information about the owner window
*/ */
XSetSelectionOwner(nxagentDisplay, lastSelectionOwner[i].selection, serverWindow, CurrentTime); XSetSelectionOwner(nxagentDisplay, lastSelectionOwner[i].selection, serverWindow, CurrentTime);
lastSelectionOwner[i].client = pSelection->client; /*
lastSelectionOwner[i].window = pSelection->window; * The real owner window (inside nxagent) is stored in
lastSelectionOwner[i].windowPtr = pSelection->pWin; * lastSelectionOwner.window. lastSelectionOwner.windowPtr
lastSelectionOwner[i].lastTimeChanged = GetTimeInMillis(); * points to the struct that contains all information about the
* owner window.
*/
nxagentStoreSelectionOwner(&lastSelectionOwner[i], pSelection);
} }
} }
...@@ -1410,12 +1598,9 @@ FIXME ...@@ -1410,12 +1598,9 @@ FIXME
void nxagentNotifyConvertFailure(ClientPtr client, Window requestor, void nxagentNotifyConvertFailure(ClientPtr client, Window requestor,
Atom selection, Atom target, Time time) Atom selection, Atom target, Time time)
{ {
xEvent x; /*
* Check if the client is still valid.
/* */
FIXME: Why this pointer can be not a valid
client pointer?
*/
if (clients[client -> index] != client) if (clients[client -> index] != client)
{ {
#ifdef WARNING #ifdef WARNING
...@@ -1425,43 +1610,33 @@ FIXME: Why this pointer can be not a valid ...@@ -1425,43 +1610,33 @@ FIXME: Why this pointer can be not a valid
return; return;
} }
memset(&x, 0, sizeof(xEvent)); SendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
x.u.u.type = SelectionNotify;
x.u.selectionNotify.time = time;
x.u.selectionNotify.requestor = requestor;
x.u.selectionNotify.selection = selection;
x.u.selectionNotify.target = target;
x.u.selectionNotify.property = None;
(void) TryClientEvents(client, &x, 1, NoEventMask,
NoEventMask , NullGrab);
} }
/*
* This is called from dix (ProcConvertSelection) if an nxagent client
* issues a ConvertSelection request. So all the Atoms are internal
* return codes:
* 0: let dix process the request
* 1: don't let dix process the request
*/
int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection, int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
Window requestor, Atom property, Atom target, Time time) Window requestor, Atom property, Atom target, Time time)
{ {
const char *strTarget;
int i;
if (agentClipboardStatus != 1 || if (agentClipboardStatus != 1 ||
nxagentOption(Clipboard) == ClipboardServer) nxagentOption(Clipboard) == ClipboardServer)
{ {
return 0; return 0;
} }
/* for (int i = 0; i < nxagentMaxSelections; i++)
* There is a client owner on the agent side, let normal stuff happen.
*/
/*
* Only for PRIMARY and CLIPBOARD selections.
*/
for (i = 0; i < nxagentMaxSelections; i++)
{ {
if ((selection == CurrentSelections[i].selection) && if ((selection == CurrentSelections[i].selection) &&
(lastSelectionOwner[i].client != NULL)) (lastSelectionOwner[i].client != NULL))
{ {
/*
* There is a client owner on the agent side, let normal dix stuff happen.
*/
return 0; return 0;
} }
} }
...@@ -1498,111 +1673,118 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection, ...@@ -1498,111 +1673,118 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
} }
} }
#ifdef TEST #if defined(TEST) || defined(DEBUG)
fprintf(stderr, "%s: client [%d] ask for sel [%s] " fprintf(stderr, "%s: client [%d] requests sel [%s] "
"on window [%x] prop [%s] target [%s].\n", __func__, "on window [%x] prop [%d][%s] target [%d][%s].\n", __func__,
client -> index, validateString(NameForAtom(selection)), requestor, CLINDEX(client), validateString(NameForAtom(selection)), requestor,
validateString(NameForAtom(property)), validateString(NameForAtom(target))); property, validateString(NameForAtom(property)),
target, validateString(NameForAtom(target)));
#endif #endif
strTarget = NameForAtom(target); const char *strTarget = NameForAtom(target);
if (strTarget == NULL) if (strTarget == NULL)
{ {
#ifdef DEBUG
fprintf(stderr, "%s: cannot find name for target Atom [%d] - returning\n", __func__, target);
#endif
return 1; return 1;
} }
/*
* The selection request target is TARGETS. The requestor is asking
* for a list of supported data formats. Currently there's 4 of them.
*
* FIXME: I am wondering if we should align this with
* nxagentRequestSelection, where we only report one format.
*/
if (target == clientTARGETS) if (target == clientTARGETS)
{ {
Atom xa_STRING[4];
xEvent x;
/* --- Order changed by dimbor (prevent sending COMPOUND_TEXT to client --- */ /* --- Order changed by dimbor (prevent sending COMPOUND_TEXT to client --- */
xa_STRING[0] = XA_STRING; Atom targets[] = {XA_STRING, clientUTF8_STRING, clientTEXT, clientCOMPOUND_TEXT};
xa_STRING[1] = clientUTF8_STRING; int numTargets = 4;
xa_STRING[2] = clientTEXT;
xa_STRING[3] = clientCOMPOUND_TEXT; #ifdef DEBUG
fprintf(stderr, "%s: available targets:\n", __func__);
for (int i = 0; i < numTargets; i++)
fprintf(stderr, "%s: %s\n", __func__, NameForAtom(targets[i]));
fprintf(stderr, "\n");
#endif
ChangeWindowProperty(pWin, ChangeWindowProperty(pWin,
property, property,
MakeAtom("ATOM", 4, 1), MakeAtom("ATOM", 4, 1),
sizeof(Atom)*8, sizeof(Atom)*8,
PropModeReplace, PropModeReplace,
4, numTargets,
&xa_STRING, 1); &targets,
1);
memset(&x, 0, sizeof(xEvent));
x.u.u.type = SelectionNotify;
x.u.selectionNotify.time = time;
x.u.selectionNotify.requestor = requestor;
x.u.selectionNotify.selection = selection;
x.u.selectionNotify.target = target;
x.u.selectionNotify.property = property;
(void) TryClientEvents(client, &x, 1, NoEventMask, SendSelectionNotifyEventToClient(client, time, requestor, selection, target, property);
NoEventMask , NullGrab);
return 1; return 1;
} }
/*
* Section 2.6.2 of the ICCCM states:
* "TIMESTAMP - To avoid some race conditions, it is important
* that requestors be able to discover the timestamp the owner
* used to acquire ownership. Until and unless the protocol is
* changed so that a GetSelectionOwner request returns the
* timestamp used to acquire ownership, selection owners must
* support conversion to TIMESTAMP, returning the timestamp they
* used to obtain the selection."
*/
if (target == MakeAtom("TIMESTAMP", 9, 1)) if (target == MakeAtom("TIMESTAMP", 9, 1))
{ {
int i = 0; int i = nxagentFindCurrentSelectionIndex(selection);
while ((i < NumCurrentSelections) &&
CurrentSelections[i].selection != selection) i++;
if (i < NumCurrentSelections) if (i < NumCurrentSelections)
{ {
xEvent x; /*
* "If the specified property is not None, the owner should place
* the data resulting from converting the selection into the
* specified property on the requestor window and should set the
* property's type to some appropriate value, which need not be
* the same as the specified target."
*/
ChangeWindowProperty(pWin, ChangeWindowProperty(pWin,
property, property,
target, XA_INTEGER,
32, 32,
PropModeReplace, PropModeReplace,
1, 1,
(unsigned char *) &lastSelectionOwner[i].lastTimeChanged, (unsigned char *) &lastSelectionOwner[i].lastTimeChanged,
1); 1);
memset(&x, 0, sizeof(xEvent)); SendSelectionNotifyEventToClient(client, time, requestor, selection, target, property);
x.u.u.type = SelectionNotify;
x.u.selectionNotify.time = time;
x.u.selectionNotify.requestor = requestor;
x.u.selectionNotify.selection = selection;
x.u.selectionNotify.target = target;
x.u.selectionNotify.property = property;
(void) TryClientEvents(client, &x, 1, NoEventMask,
NoEventMask , NullGrab);
return 1; return 1;
} }
} }
#ifdef DEBUG
if (lastClientClientPtr == client && (GetTimeInMillis() - lastClientReqTime < 5000)) if (lastClientClientPtr == client && (GetTimeInMillis() - lastClientReqTime < 5000))
{ {
/* /*
* The same client made consecutive requests * The same client made consecutive requests of clipboard content
* of clipboard contents with less than 5 * with less than 5 seconds time interval between them.
* seconds time interval between them.
*/ */
#ifdef DEBUG
fprintf(stderr, "%s: Consecutives request from client [%p] selection [%u] " fprintf(stderr, "%s: Consecutives request from client [%p] selection [%u] "
"elapsed time [%u] clientAccum [%d]\n", __func__, (void *) client, selection, "elapsed time [%u] clientAccum [%d]\n", __func__, (void *) client, selection,
GetTimeInMillis() - lastClientReqTime, clientAccum); GetTimeInMillis() - lastClientReqTime, clientAccum);
#endif
clientAccum++; clientAccum++;
} }
else else
{ {
/* reset clientAccum as now another client requested the clipboard content */
if (lastClientClientPtr != client) if (lastClientClientPtr != client)
{ {
clientAccum = 0; clientAccum = 0;
} }
} }
#endif
if ((target == clientTEXT) || if ((target == clientTEXT) ||
(target == XA_STRING) || (target == XA_STRING) ||
...@@ -1611,6 +1793,10 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection, ...@@ -1611,6 +1793,10 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
{ {
lastClientWindowPtr = pWin; lastClientWindowPtr = pWin;
SetClientSelectionStage(None); SetClientSelectionStage(None);
/*
* store the original requestor, we need that later after
* serverCutProperty contains the desired selection content
*/
lastClientRequestor = requestor; lastClientRequestor = requestor;
lastClientClientPtr = client; lastClientClientPtr = client;
lastClientTime = time; lastClientTime = time;
...@@ -1618,25 +1804,41 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection, ...@@ -1618,25 +1804,41 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
lastClientSelection = selection; lastClientSelection = selection;
lastClientTarget = target; lastClientTarget = target;
lastClientReqTime = (GetTimeInMillis() - lastClientReqTime) > 5000 ? /* if the last client request time is more than 5s ago update it. Why? */
GetTimeInMillis() : lastClientReqTime; if ((GetTimeInMillis() - lastClientReqTime) > 5000)
lastClientReqTime = GetTimeInMillis();
if (selection == MakeAtom("CLIPBOARD", 9, 0)) if (selection == MakeAtom("CLIPBOARD", 9, 0))
{ {
selection = lastSelectionOwner[nxagentClipboardSelection].selection; selection = lastSelectionOwner[nxagentClipboardSelection].selection;
} }
/*
* we only convert to either UTF8 or XA_STRING, despite accepting
* TEXT and COMPOUND_TEXT.
*/
if (target == clientUTF8_STRING) if (target == clientUTF8_STRING)
{ {
#ifdef DEBUG
fprintf(stderr, "%s: Sending XConvertSelection with target [%d][UTF8_STRING], property [%d][NX_CUT_BUFFER_SERVER]\n", __func__,
serverUTF8_STRING, serverCutProperty);
#endif
XConvertSelection(nxagentDisplay, selection, serverUTF8_STRING, serverCutProperty, XConvertSelection(nxagentDisplay, selection, serverUTF8_STRING, serverCutProperty,
serverWindow, CurrentTime); serverWindow, CurrentTime);
} }
else else
{ {
#ifdef DEBUG
fprintf(stderr, "%s: Sending XConvertSelection with target [%d][%s], property [%d][NX_CUT_BUFFER_SERVER]\n", __func__,
XA_STRING, validateString(NameForAtom(XA_STRING)), serverCutProperty);
#endif
XConvertSelection(nxagentDisplay, selection, XA_STRING, serverCutProperty, XConvertSelection(nxagentDisplay, selection, XA_STRING, serverCutProperty,
serverWindow, CurrentTime); serverWindow, CurrentTime);
} }
/* FIXME: check returncode of XConvertSelection */
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: Sent XConvertSelection with target=[%s], property [%s]\n", __func__, fprintf(stderr, "%s: Sent XConvertSelection with target=[%s], property [%s]\n", __func__,
validateString(NameForAtom(target)), validateString(NameForAtom(property))); validateString(NameForAtom(target)), validateString(NameForAtom(property)));
...@@ -1646,26 +1848,32 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection, ...@@ -1646,26 +1848,32 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
} }
else else
{ {
xEvent x; /* deny request */
SendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
#ifdef DEBUG
fprintf(stderr, "%s: Xserver generates a SelectionNotify event "
"to the requestor with property None.\n", __func__);
#endif
memset(&x, 0, sizeof(xEvent));
x.u.u.type = SelectionNotify;
x.u.selectionNotify.time = time;
x.u.selectionNotify.requestor = requestor;
x.u.selectionNotify.selection = selection;
x.u.selectionNotify.target = target;
x.u.selectionNotify.property = None;
(void) TryClientEvents(client, &x, 1, NoEventMask, NoEventMask , NullGrab);
return 1; return 1;
} }
return 0; return 0;
} }
/*
* This is _only_ called from ProcSendEvent in NXevents.c. It is used
* to send a SelectionNotify event to our server window which will
* trigger the dispatch loop in Events.c to run
* nxagentHandleSelectionNotifyFromXServer which in turn will take
* care of transferring the selection content from the owning client
* to to a property of the server window.
*
* Returning 1 here means the client request will not be further
* handled by dix. Returning 0 means a SelectionNotify event being
* pushed out to our clients.
*
* From https://tronche.com/gui/x/xlib/events/client-communication/selection.html:
* "This event is generated by the X server in response to a
* ConvertSelection protocol request when there is no owner for the
* selection. When there is an owner, it should be generated by the
* owner of the selection by using XSendEvent()."
*/
int nxagentSendNotify(xEvent *event) int nxagentSendNotify(xEvent *event)
{ {
#ifdef DEBUG #ifdef DEBUG
...@@ -1681,56 +1889,92 @@ int nxagentSendNotify(xEvent *event) ...@@ -1681,56 +1889,92 @@ int nxagentSendNotify(xEvent *event)
} }
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: property is [%d][%s].\n", __func__, event->u.selectionNotify.property, NameForAtom(event->u.selectionNotify.property)); fprintf(stderr, "%s: property is [%d][%s].\n", __func__,
event->u.selectionNotify.property,
NameForAtom(event->u.selectionNotify.property));
fprintf(stderr, "%s: requestor is [0x%x].\n", __func__, event->u.selectionNotify.requestor);
fprintf(stderr, "%s: lastServerRequestor is [0x%x].\n", __func__, lastServerRequestor);
#endif #endif
if (event->u.selectionNotify.property == clientCutProperty) /*
* If we have nested sessions there are situations where we do not
* need to send out anything to the real X server because
* communication happens completely between our own clients (some of
* which can be nxagents themselves). In that case we return 0 (tell
* dix to go on) and do nothing!
*/
if (event->u.selectionNotify.property == clientCutProperty && lastServerRequestor != None)
{ {
XSelectionEvent x;
int result;
/* /*
* Setup selection notify event to real server. * Setup selection notify event to real server.
*
* .property must be a server-side Atom. As this property is only
* set on our serverWindow and normally there are no other
* properties except serverCutProperty, the only thing we need to
* ensure is that the internal Atom clientCutProperty must differ
* from the server-side serverCutProperty Atom. The actual name is
* not important. To be clean here we use a seperate
* serverClientCutProperty.
*/ */
memset(&x, 0, sizeof(XSelectionEvent)); XSelectionEvent eventSelection = {
x.type = SelectionNotify; .requestor = serverWindow,
x.send_event = True; .selection = event->u.selectionNotify.selection,
x.display = nxagentDisplay; .target = event->u.selectionNotify.target,
x.requestor = serverWindow; .property = serverClientCutProperty,
.time = CurrentTime,
};
/* /*
* On real server, the right CLIPBOARD atom is * On the real server, the right CLIPBOARD atom is
* XInternAtom(nxagentDisplay, "CLIPBOARD", 1). * XInternAtom(nxagentDisplay, "CLIPBOARD", 1), which is stored in
* lastSelectionOwner[nxagentClipboardSelection].selection. For
* PRIMARY there's nothing to map because that is identical on all
* X servers (defined in Xatom.h).
*/ */
if (event->u.selectionNotify.selection == MakeAtom("CLIPBOARD", 9, 0)) if (event->u.selectionNotify.selection == MakeAtom("CLIPBOARD", 9, 0))
{ {
x.selection = lastSelectionOwner[nxagentClipboardSelection].selection; eventSelection.selection = lastSelectionOwner[nxagentClipboardSelection].selection;
} }
/*
* .target must be translated, too, as a client on the real
* server is requested to fill our property and it needs to know
* the format.
*/
if (event->u.selectionNotify.target == clientUTF8_STRING)
{
eventSelection.target = serverUTF8_STRING;
}
else if (event->u.selectionNotify.target == clientTEXT)
{
eventSelection.target = serverTEXT;
}
/*else if (event->u.selectionNotify.target == clientCOMPOUND_TEXT)
{
eventSelection.target = serverCOMPOUND_TEXT;
}*/
else else
{ {
x.selection = event->u.selectionNotify.selection; eventSelection.target = XA_STRING;
} }
x.target = event->u.selectionNotify.target;
x.property = event->u.selectionNotify.property;
x.time = CurrentTime;
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: Propagating clientCutProperty to requestor [%p].\n", __func__, (void *)x.requestor); fprintf(stderr, "%s: mapping local to remote Atom: [%d] -> [%ld] [%s]\n", __func__,
event->u.selectionNotify.selection, eventSelection.selection,
NameForAtom(event->u.selectionNotify.selection));
fprintf(stderr, "%s: mapping local to remote Atom: [%d] -> [%ld] [%s]\n", __func__,
event->u.selectionNotify.target, eventSelection.target,
NameForAtom(event->u.selectionNotify.target));
fprintf(stderr, "%s: mapping local to remote Atom: [%d] -> [%ld] [%s]\n", __func__,
event->u.selectionNotify.property, eventSelection.property,
NameForAtom(event->u.selectionNotify.property));
#endif #endif
result = XSendEvent (nxagentDisplay, x.requestor, False, SendSelectionNotifyEventToServer(&eventSelection);
0L, (XEvent *) &x);
#ifdef DEBUG
fprintf(stderr, "%s: XSendEvent() returned [%s]\n", __func__, GetXErrorString(result));
#endif
if (result == BadValue || result == BadWindow)
{
fprintf (stderr, "%s: XSendEvent failed.\n", __func__);
}
return 1; return 1;
} }
...@@ -1740,43 +1984,38 @@ int nxagentSendNotify(xEvent *event) ...@@ -1740,43 +1984,38 @@ int nxagentSendNotify(xEvent *event)
return 0; return 0;
} }
WindowPtr nxagentGetClipboardWindow(Atom property, WindowPtr pWin) WindowPtr nxagentGetClipboardWindow(Atom property)
{ {
int i = 0; int i = nxagentFindLastSelectionOwnerIndex(nxagentLastRequestedSelection);
#ifdef DEBUG
fprintf(stderr, "%s: Got called, property [%d][%s] window [%p].\n", __func__, property, NameForAtom(property), (void *)pWin);
#endif
while ((i < nxagentMaxSelections) &&
(lastSelectionOwner[i].selection != nxagentLastRequestedSelection))
{
i++;
}
if ((i < nxagentMaxSelections) && (property == clientCutProperty) && if ((i < nxagentMaxSelections) && (property == clientCutProperty) &&
(lastSelectionOwner[i].windowPtr != NULL)) (lastSelectionOwner[i].windowPtr != NULL))
{ {
#ifdef DEBUG #ifdef DEBUG
fprintf(stderr, "%s: Returning last clipboard owner window [%p].\n", __func__, (void *)lastSelectionOwner[i].windowPtr); fprintf(stderr, "%s: Returning last [%d] selection owner window [%p] (0x%x).\n", __func__,
lastSelectionOwner[i].selection,
(void *)lastSelectionOwner[i].windowPtr, WINDOWID(lastSelectionOwner[i].windowPtr));
#endif #endif
return lastSelectionOwner[i].windowPtr; return lastSelectionOwner[i].windowPtr;
} }
else else
{ {
#ifdef DEBUG return NULL;
fprintf(stderr, "%s: Returning original target window [%p].\n", __func__, (void *)pWin);
#endif
return pWin;
} }
}
void nxagentInitSelectionOwner(SelectionOwner *owner, Atom selection)
{
owner->selection = selection;
owner->client = NullClient;
owner->window = screenInfo.screens[0]->root->drawable.id;
owner->windowPtr = NULL;
owner->lastTimeChanged = GetTimeInMillis();
} }
int nxagentInitClipboard(WindowPtr pWin) int nxagentInitClipboard(WindowPtr pWin)
{ {
int i;
Window iWindow = nxagentWindow(pWin); Window iWindow = nxagentWindow(pWin);
#ifdef DEBUG #ifdef DEBUG
...@@ -1792,20 +2031,10 @@ int nxagentInitClipboard(WindowPtr pWin) ...@@ -1792,20 +2031,10 @@ int nxagentInitClipboard(WindowPtr pWin)
FatalError("nxagentInitClipboard: Failed to allocate memory for the clipboard selections.\n"); FatalError("nxagentInitClipboard: Failed to allocate memory for the clipboard selections.\n");
} }
nxagentClipboardAtom = nxagentAtoms[10]; /* CLIPBOARD */ serverTIMESTAMP = nxagentAtoms[11]; /* TIMESTAMP */
nxagentTimestampAtom = nxagentAtoms[11]; /* TIMESTAMP */
lastSelectionOwner[nxagentPrimarySelection].selection = XA_PRIMARY; nxagentInitSelectionOwner(&lastSelectionOwner[nxagentPrimarySelection], XA_PRIMARY);
lastSelectionOwner[nxagentPrimarySelection].client = NullClient; nxagentInitSelectionOwner(&lastSelectionOwner[nxagentClipboardSelection], nxagentAtoms[10]); /* CLIPBOARD */
lastSelectionOwner[nxagentPrimarySelection].window = screenInfo.screens[0]->root->drawable.id;
lastSelectionOwner[nxagentPrimarySelection].windowPtr = NULL;
lastSelectionOwner[nxagentPrimarySelection].lastTimeChanged = GetTimeInMillis();
lastSelectionOwner[nxagentClipboardSelection].selection = nxagentClipboardAtom;
lastSelectionOwner[nxagentClipboardSelection].client = NullClient;
lastSelectionOwner[nxagentClipboardSelection].window = screenInfo.screens[0]->root->drawable.id;
lastSelectionOwner[nxagentClipboardSelection].windowPtr = NULL;
lastSelectionOwner[nxagentClipboardSelection].lastTimeChanged = GetTimeInMillis();
#ifdef NXAGENT_TIMESTAMP #ifdef NXAGENT_TIMESTAMP
{ {
...@@ -1827,6 +2056,8 @@ int nxagentInitClipboard(WindowPtr pWin) ...@@ -1827,6 +2056,8 @@ int nxagentInitClipboard(WindowPtr pWin)
serverTARGETS = nxagentAtoms[6]; /* TARGETS */ serverTARGETS = nxagentAtoms[6]; /* TARGETS */
serverTEXT = nxagentAtoms[7]; /* TEXT */ serverTEXT = nxagentAtoms[7]; /* TEXT */
serverUTF8_STRING = nxagentAtoms[12]; /* UTF8_STRING */ serverUTF8_STRING = nxagentAtoms[12]; /* UTF8_STRING */
/* see nxagentSendNotify for an explanation */
serverClientCutProperty = nxagentAtoms[15]; /* NX_CUT_BUFFER_CLIENT */
if (serverCutProperty == None) if (serverCutProperty == None)
{ {
...@@ -1858,7 +2089,7 @@ int nxagentInitClipboard(WindowPtr pWin) ...@@ -1858,7 +2089,7 @@ int nxagentInitClipboard(WindowPtr pWin)
fprintf(stderr, "%s: Registering for XFixesSelectionNotify events.\n", __func__); fprintf(stderr, "%s: Registering for XFixesSelectionNotify events.\n", __func__);
#endif #endif
for (i = 0; i < nxagentMaxSelections; i++) for (int i = 0; i < nxagentMaxSelections; i++)
{ {
XFixesSelectSelectionInput(nxagentDisplay, iWindow, XFixesSelectSelectionInput(nxagentDisplay, iWindow,
lastSelectionOwner[i].selection, lastSelectionOwner[i].selection,
...@@ -1898,7 +2129,7 @@ int nxagentInitClipboard(WindowPtr pWin) ...@@ -1898,7 +2129,7 @@ int nxagentInitClipboard(WindowPtr pWin)
* Only for PRIMARY and CLIPBOARD selections. * Only for PRIMARY and CLIPBOARD selections.
*/ */
for (i = 0; i < nxagentMaxSelections; i++) for (int i = 0; i < nxagentMaxSelections; i++)
{ {
if (lastSelectionOwner[i].client && lastSelectionOwner[i].window) if (lastSelectionOwner[i].client && lastSelectionOwner[i].window)
{ {
......
...@@ -64,6 +64,7 @@ extern int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom select ...@@ -64,6 +64,7 @@ extern int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom select
void nxagentClearSelection(); void nxagentClearSelection();
void nxagentRequestSelection(); void nxagentRequestSelection();
void nxagentNotifySelection(); void nxagentHandleSelectionNotifyFromXServer();
int nxagentFindCurrentSelectionIndex(Atom sel);
#endif /* __Clipboard_H__ */ #endif /* __Clipboard_H__ */
...@@ -944,7 +944,7 @@ void nxagentDispatchEvents(PredicateFuncPtr predicate) ...@@ -944,7 +944,7 @@ void nxagentDispatchEvents(PredicateFuncPtr predicate)
fprintf(stderr, "nxagentDispatchEvents: Going to handle new SelectionNotify event.\n"); fprintf(stderr, "nxagentDispatchEvents: Going to handle new SelectionNotify event.\n");
#endif #endif
nxagentNotifySelection(&X); nxagentHandleSelectionNotifyFromXServer(&X);
break; break;
} }
...@@ -2933,12 +2933,7 @@ int nxagentHandleXFixesSelectionNotify(XEvent *X) ...@@ -2933,12 +2933,7 @@ int nxagentHandleXFixesSelectionNotify(XEvent *X)
if (SelectionCallback) if (SelectionCallback)
{ {
int i = 0; int i = nxagentFindCurrentSelectionIndex(local);
while ((i < NumCurrentSelections) &&
CurrentSelections[i].selection != local)
i++;
if (i < NumCurrentSelections) if (i < NumCurrentSelections)
{ {
SelectionInfoRec info; SelectionInfoRec info;
......
...@@ -691,11 +691,7 @@ ProcConvertSelection(register ClientPtr client) ...@@ -691,11 +691,7 @@ ProcConvertSelection(register ClientPtr client)
(stuff->selection == MakeAtom("CLIPBOARD", 9, 0))) && (stuff->selection == MakeAtom("CLIPBOARD", 9, 0))) &&
nxagentOption(Clipboard) != ClipboardNone) nxagentOption(Clipboard) != ClipboardNone)
{ {
int i = 0; int i = nxagentFindCurrentSelectionIndex(stuff->selection);
while ((i < NumCurrentSelections) &&
CurrentSelections[i].selection != stuff->selection) i++;
if ((i < NumCurrentSelections) && (CurrentSelections[i].window != None)) if ((i < NumCurrentSelections) && (CurrentSelections[i].window != None))
{ {
if (nxagentConvertSelection(client, pWin, stuff->selection, stuff->requestor, if (nxagentConvertSelection(client, pWin, stuff->selection, stuff->requestor,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment