aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Gabriel <mike.gabriel@das-netzwerkteam.de>2019-09-29 17:58:09 +0200
committerMike Gabriel <mike.gabriel@das-netzwerkteam.de>2019-09-29 17:58:09 +0200
commit5405447e5a0ec582a474a0e506265475bd4fd5a8 (patch)
tree96a4ed65f1a16eadf46816d6b55d96fd976be421
parent3a3a3373c36b5af6208ee5fdfe25e64b5766b75a (diff)
parent72c022406f83d78425daae3037aebd2123f66293 (diff)
downloadnx-libs-5405447e5a0ec582a474a0e506265475bd4fd5a8.tar.gz
nx-libs-5405447e5a0ec582a474a0e506265475bd4fd5a8.tar.bz2
nx-libs-5405447e5a0ec582a474a0e506265475bd4fd5a8.zip
Merge branch 'uli42-pr/clipboard_overhaul' into 3.6.x
Attributes GH PR #846: https://github.com/ArcticaProject/nx-libs/pull/846
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Atoms.c1
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Atoms.h2
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Clipboard.c1397
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Clipboard.h3
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Events.c9
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/NXdispatch.c6
6 files changed, 821 insertions, 597 deletions
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Atoms.c b/nx-X11/programs/Xserver/hw/nxagent/Atoms.c
index 1c48df61c..d9d203faa 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Atoms.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Atoms.c
@@ -90,6 +90,7 @@ static char *nxagentAtomNames[NXAGENT_NUMBER_OF_ATOMS + 1] =
"UTF8_STRING", /* 12 */
"_NET_WM_STATE", /* 13 */
"_NET_WM_STATE_FULLSCREEN", /* 14 */
+ "NX_CUT_BUFFER_CLIENT", /* 15 */
NULL,
NULL
};
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Atoms.h b/nx-X11/programs/Xserver/hw/nxagent/Atoms.h
index 08eb1cfff..cbbb7bd1d 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Atoms.h
+++ b/nx-X11/programs/Xserver/hw/nxagent/Atoms.h
@@ -30,7 +30,7 @@
#include "../../include/window.h"
#include "screenint.h"
-#define NXAGENT_NUMBER_OF_ATOMS 16
+#define NXAGENT_NUMBER_OF_ATOMS 17
extern Atom nxagentAtoms[NXAGENT_NUMBER_OF_ATOMS];
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Clipboard.c b/nx-X11/programs/Xserver/hw/nxagent/Clipboard.c
index fe5c7d2c9..5f7bdab4e 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Clipboard.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Clipboard.c
@@ -68,7 +68,9 @@ extern Selection *CurrentSelections;
int nxagentLastClipboardClient = -1;
static int agentClipboardStatus;
+#ifdef DEBUG
static int clientAccum;
+#endif
Atom serverCutProperty;
Atom clientCutProperty;
@@ -80,18 +82,21 @@ const int nxagentMaxSelections = 2;
typedef struct _SelectionOwner
{
- Atom selection;
- ClientPtr client;
- Window window;
- WindowPtr windowPtr;
- Time lastTimeChanged;
-
+ Atom selection; /* _external_ Atom */
+ ClientPtr client; /* internal client */
+ Window window; /* internal window id */
+ WindowPtr windowPtr; /* internal window struct */
+ Time lastTimeChanged; /* internal time */
} 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 Atom nxagentLastRequestedSelection;
-static Atom nxagentClipboardAtom;
-static Atom nxagentTimestampAtom;
/*
* Needed to handle the notify selection event to
@@ -126,8 +131,10 @@ static Atom lastServerTarget;
static Time lastServerTime;
static Atom serverTARGETS;
+static Atom serverTIMESTAMP;
static Atom serverTEXT;
static Atom serverUTF8_STRING;
+static Atom serverClientCutProperty;
static Atom clientTARGETS;
static Atom clientTEXT;
static Atom clientCOMPOUND_TEXT;
@@ -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 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
#define SetClientSelectionStage(stage) do {lastClientStage = SelectionStage##stage;} while (0)
#define PrintClientSelectionStage()
@@ -201,11 +210,13 @@ XFixesAgentInfoRec nxagentXFixesInfo = { -1, -1, -1, 0 };
extern Display *nxagentDisplay;
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 nxagentCollectPropertyEvent(int resource);
void nxagentResetSelectionOwner(void);
-WindowPtr nxagentGetClipboardWindow(Atom property, WindowPtr pWin);
+WindowPtr nxagentGetClipboardWindow(Atom property);
void nxagentNotifyConvertFailure(ClientPtr client, Window requestor,
Atom selection, Atom target, Time time);
int nxagentSendNotify(xEvent *event);
@@ -222,16 +233,19 @@ void nxagentPrintSelectionStat(int sel)
#ifdef CLIENTIDS
fprintf(stderr, " lastSelectionOwner[].client [%p] index [%d] PID [%d] Cmd [%s]\n",
(void *)lOwner.client,
- lOwner.client ? lOwner.client->index : -1,
+ CLINDEX(lOwner.client),
GetClientPid(lOwner.client),
GetClientCmdName(lOwner.client));
#else
fprintf(stderr, " lastSelectionOwner[].client [%p] index [%d]\n",
(void *)lOwner.client,
- lOwner.client ? lOwner.client->index : -1);
+ CLINDEX(lOwner.client));
#endif
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);
/*
@@ -250,13 +264,13 @@ void nxagentPrintSelectionStat(int sel)
#ifdef CLIENTIDS
fprintf(stderr, " CurrentSelections[].client [%p] index [%d] PID [%d] Cmd [%s]\n",
(void *)curSel.client,
- curSel.client ? curSel.client->index : -1,
+ CLINDEX(curSel.client),
GetClientPid(curSel.client),
GetClientCmdName(curSel.client));
#else
fprintf(stderr, " CurrentSelections[].client [%p] index [%d]\n",
(void *)curSel.client,
- curSel.client ? curSel.client->index : -1);
+ CLINDEX(curSel.client);
#endif
fprintf(stderr, " CurrentSelections[].window [0x%x]\n", curSel.window);
return;
@@ -298,7 +312,10 @@ void nxagentPrintClipboardStat(char *header)
fprintf(stderr, " lastServerTime (Time) [%u]\n", lastServerTime);
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, " lastClientRequestor (Window) [0x%x]\n", lastClientRequestor);
fprintf(stderr, " lastClientProperty (Atom) [% 4d][%s]\n", lastClientProperty, NameForAtom(lastClientProperty));
@@ -323,11 +340,11 @@ void nxagentPrintClipboardStat(char *header)
fprintf(stderr, " serverUTF8_STRING [% 4d][%s]\n", serverUTF8_STRING, s);
SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverCutProperty);
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);
- fprintf(stderr, " nxagentClipboardAtom [% 4d][%s]\n", nxagentClipboardAtom, s);
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, nxagentTimestampAtom);
- fprintf(stderr, " nxagentTimestampAtom [% 4d][%s]\n", nxagentTimestampAtom, s);
+ SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverTIMESTAMP);
+ fprintf(stderr, " serverTIMESTAMP [% 4d][%s]\n", serverTIMESTAMP, s);
fprintf(stderr, "Atoms (inside nxagent)\n");
fprintf(stderr, " clientTARGETS [% 4d][%s]\n", clientTARGETS, NameForAtom(clientTARGETS));
@@ -352,6 +369,85 @@ int GetWindowProperty(WindowPtr pWin, Atom property, long longOffset, long longL
unsigned long *nItems, unsigned long *bytesAfter,
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)
{
if (target == XA_STRING)
@@ -383,6 +479,13 @@ Bool nxagentValidServerTargets(Atom target)
#endif
return False;
}
+ else if (target == serverTIMESTAMP)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: special target [TIMESTAMP].\n", __func__);
+ #endif
+ return False;
+ }
#ifdef DEBUG
fprintf(stderr, "%s: invalid target [%u].\n", __func__, target);
@@ -390,11 +493,28 @@ Bool nxagentValidServerTargets(Atom target)
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)
{
#ifdef DEBUG
- fprintf(stderr, "%s: Called with client [%p] window [%p].\n", __func__,
- (void *) pClient, (void *) pWindow);
+ fprintf(stderr, "%s: Called with client [%p] index [%d] window [%p] ([0x%x]).\n", __func__,
+ (void *) pClient, CLINDEX(pClient), (void *) pWindow, WINDOWID(pWindow));
#endif
nxagentPrintClipboardStat("before nxagentClearClipboard");
@@ -413,10 +533,9 @@ void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow)
(void *) pClient, (void *) pWindow);
#endif
- lastSelectionOwner[i].client = NULL;
- lastSelectionOwner[i].window = None;
+ /* FIXME: why is windowPtr not cleared in the function? */
+ nxagentClearSelectionOwner(&lastSelectionOwner[i]);
lastSelectionOwner[i].windowPtr = NULL;
- lastSelectionOwner[i].lastTimeChanged = GetTimeInMillis();
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
@@ -434,10 +553,35 @@ void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow)
nxagentPrintClipboardStat("after nxagentClearClipboard");
}
-void nxagentClearSelection(XEvent *X)
+int nxagentFindLastSelectionOwnerIndex(Atom sel)
{
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
fprintf(stderr, "%s: SelectionClear event for selection [%lu].\n", __func__, X->xselectionclear.selection);
#endif
@@ -450,34 +594,31 @@ void nxagentClearSelection(XEvent *X)
return;
}
- while ((i < nxagentMaxSelections) &&
- (lastSelectionOwner[i].selection != X->xselectionclear.selection))
- {
- i++;
- }
+ int i = nxagentFindLastSelectionOwnerIndex(X->xselectionclear.selection);
if (i < nxagentMaxSelections)
{
if (lastSelectionOwner[i].client != NULL)
{
- xEvent x;
- memset(&x, 0, sizeof(xEvent));
+ /* send a SelectionClear event to (our) previous owner */
+ xEvent x = {0};
x.u.u.type = SelectionClear;
x.u.selectionClear.time = GetTimeInMillis();
x.u.selectionClear.window = lastSelectionOwner[i].window;
x.u.selectionClear.atom = CurrentSelections[i].selection;
- (void) TryClientEvents(lastSelectionOwner[i].client, &x, 1,
- NoEventMask, NoEventMask,
- NullGrab);
+ SendEventToClient(lastSelectionOwner[i].client, &x);
}
+ /*
+ * 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].client = NullClient;
- lastSelectionOwner[i].client = NULL;
- lastSelectionOwner[i].window = None;
- lastSelectionOwner[i].lastTimeChanged = GetTimeInMillis();
+ nxagentClearSelectionOwner(&lastSelectionOwner[i]);
}
lastClientWindowPtr = NULL;
@@ -485,13 +626,63 @@ void nxagentClearSelection(XEvent *X)
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 = {0};
+ XSelectionEvent eventSelection = {
+ .requestor = X->xselectionrequest.requestor,
+ .selection = X->xselectionrequest.selection,
+ .target = X->xselectionrequest.target,
+ .time = X->xselectionrequest.time,
+ .property = X->xselectionrequest.property
+ };
+
+ if (!success)
+ {
+ #ifdef DEBUG
+ 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
- fprintf(stderr, "%s: Got called.\n", __func__);
+ {
+ 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
nxagentPrintClipboardStat("before nxagentRequestSelection");
@@ -501,112 +692,122 @@ void nxagentRequestSelection(XEvent *X)
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) ||
(lastServerRequestor != None) ||
((X->xselectionrequest.selection != lastSelectionOwner[nxagentPrimarySelection].selection) &&
(X->xselectionrequest.selection != lastSelectionOwner[nxagentClipboardSelection].selection)))
{
-/*
-FIXME: Do we need this?
-
- char *strTarget;
-
- strTarget = XGetAtomName(nxagentDisplay, X->xselectionrequest.target);
-
- fprintf(stderr, "SelectionRequest event aborting sele=[%s] target=[%s]\n",
- validateString(NameForAtom(X->xselectionrequest.selection)),
- validateString(NameForAtom(X->xselectionrequest.target)));
+ if (X->xselectionrequest.target == serverTARGETS)
+ {
+ /*
+ * the selection request target is TARGETS. The requestor is
+ * asking for a list of supported data formats. Currently
+ * there's only one format we support: XA_STRING
+ *
+ * The selection does not matter here, we will return this for
+ * PRIMARY and CLIPBOARD.
+ *
+ * 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",
- validateString(NameForAtom(X->xselectionrequest.selection)), strTarget, sizeof(Atom));
+ Atom targets[] = {XA_STRING};
+ int numTargets = 1;
- SAFE_XFree(strTarget);
-*/
- memset(&eventSelection, 0, sizeof(XSelectionEvent));
- eventSelection.property = None;
+ #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
- if (X->xselectionrequest.target == serverTARGETS)
- {
- Atom xa_STRING = XA_STRING;
- XChangeProperty (nxagentDisplay,
- X->xselectionrequest.requestor,
- X->xselectionrequest.property,
- XInternAtom(nxagentDisplay, "ATOM", 0),
- sizeof(Atom)*8,
- PropModeReplace,
- (unsigned char*)&xa_STRING,
- 1);
- eventSelection.property = X->xselectionrequest.property;
+ /*
+ * pass on the requested list by setting the property provided
+ * by the requestor accordingly.
+ */
+ XChangeProperty(nxagentDisplay,
+ X->xselectionrequest.requestor,
+ X->xselectionrequest.property,
+ XInternAtom(nxagentDisplay, "ATOM", 0),
+ sizeof(Atom)*8,
+ PropModeReplace,
+ (unsigned char*)&targets,
+ numTargets);
+
+ 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,
- X->xselectionrequest.requestor,
- X->xselectionrequest.property,
- X->xselectionrequest.target,
- 32,
- PropModeReplace,
- (unsigned char *) &lastSelectionOwner[i].lastTimeChanged,
- 1);
- eventSelection.property = X->xselectionrequest.property;
+ X->xselectionrequest.requestor,
+ X->xselectionrequest.property,
+ XA_INTEGER,
+ 32,
+ PropModeReplace,
+ (unsigned char *) &lastSelectionOwner[i].lastTimeChanged,
+ 1);
+ 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
{
- fprintf(stderr, "%s: XSendEvent sent to window [0x%lx].\n", __func__,
- eventSelection.requestor);
+ /* deny the request */
+ nxagentReplyRequestSelection(X, False);
}
- #endif
-
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;
- /* FIXME: shouldn't we reset i to 0 here first? */
- while ((i < nxagentMaxSelections) &&
- (lastSelectionOwner[i].selection != X->xselectionrequest.selection))
- {
- i++;
- }
-
+ /* find the index of the requested selection */
+ int i = nxagentFindLastSelectionOwnerIndex(X->xselectionrequest.selection);
if (i < nxagentMaxSelections)
{
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,
X->xselectionrequest.target, serverCutProperty,
serverWindow, lastClientTime);
@@ -617,141 +818,121 @@ FIXME: Do we need this?
}
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 &&
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;
lastServerRequestor = X->xselectionrequest.requestor;
lastServerTarget = X->xselectionrequest.target;
+ lastServerTime = X->xselectionrequest.time;
/* by dimbor */
if (lastServerTarget != XA_STRING)
lastServerTarget = serverUTF8_STRING;
- lastServerTime = X->xselectionrequest.time;
-
- memset(&x, 0, sizeof(xEvent));
+ /* prepare the request (like XConvertSelection, but internally) */
+ xEvent x = {0};
x.u.u.type = SelectionRequest;
x.u.selectionRequest.time = GetTimeInMillis();
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.
- */
-
- 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.
+ * 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.selection = CurrentSelections[i].selection;
-
/* by dimbor (idea from zahvatov) */
if (X->xselectionrequest.target != XA_STRING)
x.u.selectionRequest.target = clientUTF8_STRING;
else
x.u.selectionRequest.target = XA_STRING;
- x.u.selectionRequest.property = clientCutProperty;
-
- (void) TryClientEvents(lastSelectionOwner[i].client, &x, 1,
- NoEventMask, NoEventMask /* CantBeFiltered */,
- NullGrab);
+ SendEventToClient(lastSelectionOwner[i].client, &x);
#ifdef DEBUG
- fprintf(stderr, "%s: Executed TryClientEvents with clientCutProperty.\n", __func__);
+ 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
}
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
- 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
- {
- fprintf(stderr, "%s: XSendEvent with property None sent to window [0x%lx].\n", __func__,
- eventSelection.requestor);
- }
- #endif
+ /* deny the request */
+ nxagentReplyRequestSelection(X, False);
}
}
}
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
+ fprintf(stderr, "%s: lastClientClientPtr is NULL - doing nothing.\n", __func__);
+ #endif
+ return;
+ }
#ifdef DEBUG
- fprintf (stderr, "%s: Sending event to client [%d].\n", __func__,
- lastClientClientPtr -> index);
+ if (success == SELECTION_SUCCESS)
+ fprintf(stderr, "%s: sending notification to client [%d], property [%d][%s]\n", __func__,
+ CLINDEX(lastClientClientPtr), lastClientProperty, NameForAtom(lastClientProperty));
+ else
+ fprintf(stderr, "%s: sending negative notification to client [%d]\n", __func__,
+ CLINDEX(lastClientClientPtr));
#endif
- memset(&x, 0, sizeof(xEvent));
- x.u.u.type = SelectionNotify;
-
- x.u.selectionNotify.time = lastClientTime;
- x.u.selectionNotify.requestor = lastClientRequestor;
- x.u.selectionNotify.selection = lastClientSelection;
- x.u.selectionNotify.target = lastClientTarget;
-
- x.u.selectionNotify.property = property;
+ SendSelectionNotifyEventToClient(lastClientClientPtr,
+ lastClientTime,
+ lastClientRequestor,
+ lastClientSelection,
+ lastClientTarget,
+ success == SELECTION_SUCCESS ? lastClientProperty : None);
- TryClientEvents(lastClientClientPtr, &x, 1, NoEventMask,
- NoEventMask , NullGrab);
-
- return;
+ /*
+ * Enable further requests from clients.
+ */
+ lastClientWindowPtr = NULL;
+ SetClientSelectionStage(None);
}
void nxagentTransferSelection(int resource)
{
- int result;
-
if (lastClientClientPtr -> index != resource)
{
#ifdef DEBUG
fprintf (stderr, "%s: WARNING! Inconsistent resource [%d] with current client [%d].\n", __func__,
- resource, lastClientClientPtr -> index);
+ resource, CLINDEX(lastClientClientPtr));
#endif
- nxagentSendSelectionNotify(None);
-
- lastClientWindowPtr = NULL;
- SetClientSelectionStage(None);
+ endTransfer(SELECTION_FAULT);
return;
}
@@ -760,6 +941,8 @@ void nxagentTransferSelection(int resource)
{
case SelectionStageQuerySize:
{
+ int result;
+
PrintClientSelectionStage();
/*
* Don't get data yet, just get size. We skip
@@ -793,23 +976,24 @@ void nxagentTransferSelection(int resource)
{
#ifdef DEBUG
fprintf (stderr, "%s: Aborting selection notify procedure for client [%d].\n", __func__,
- lastClientClientPtr -> index);
+ CLINDEX(lastClientClientPtr));
#endif
- nxagentSendSelectionNotify(None);
-
- lastClientWindowPtr = NULL;
- SetClientSelectionStage(None);
+ endTransfer(SELECTION_FAULT);
return;
}
SetClientSelectionStage(WaitSize);
+ NXFlushDisplay(nxagentDisplay, NXFlushLink);
+
break;
}
case SelectionStageQueryData:
{
+ int result;
+
PrintClientSelectionStage();
/*
@@ -846,26 +1030,27 @@ void nxagentTransferSelection(int resource)
{
#ifdef DEBUG
fprintf (stderr, "%s: Aborting selection notify procedure for client [%d].\n", __func__,
- lastClientClientPtr -> index);
+ CLINDEX(lastClientClientPtr));
#endif
- nxagentSendSelectionNotify(None);
-
- lastClientWindowPtr = NULL;
- SetClientSelectionStage(None);
+ endTransfer(SELECTION_FAULT);
return;
}
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;
}
default:
{
#ifdef DEBUG
fprintf (stderr, "%s: WARNING! Inconsistent state [%s] for client [%d].\n", __func__,
- GetClientSelectionStageString(lastClientStage), lastClientClientPtr -> index);
+ GetClientSelectionStageString(lastClientStage), CLINDEX(lastClientClientPtr));
#endif
break;
@@ -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)
{
Atom atomReturnType;
@@ -883,9 +1075,8 @@ void nxagentCollectPropertyEvent(int resource)
int result;
/*
- * We have received the notification so
- * we can safely retrieve data from the
- * client structure.
+ * We have received the notification so we can safely retrieve data
+ * from the client structure.
*/
result = NXGetCollectedProperty(nxagentDisplay,
@@ -901,168 +1092,151 @@ void nxagentCollectPropertyEvent(int resource)
if (result == 0)
{
#ifdef DEBUG
- fprintf (stderr, "%s: Failed to get reply data for client [%d].\n", __func__,
- lastClientClientPtr -> index);
+ fprintf (stderr, "%s: Failed to get reply data.\n", __func__);
#endif
- nxagentSendSelectionNotify(None);
-
- lastClientWindowPtr = NULL;
- SetClientSelectionStage(None);
-
- SAFE_XFree(pszReturnData);
- return;
+ endTransfer(SELECTION_FAULT);
}
-
- if (resultFormat != 8 && resultFormat != 16 && resultFormat != 32)
+ else if (resultFormat != 8 && resultFormat != 16 && resultFormat != 32)
{
#ifdef DEBUG
- fprintf (stderr, "%s: WARNING! Invalid property value.\n", __func__);
+ fprintf (stderr, "%s: WARNING! Invalid property format.\n", __func__);
#endif
- if (lastClientClientPtr != NULL)
- {
- nxagentSendSelectionNotify(None);
- }
-
- lastClientWindowPtr = NULL;
- SetClientSelectionStage(None);
-
- SAFE_XFree(pszReturnData);
- return;
+ endTransfer(SELECTION_FAULT);
}
-
- switch (lastClientStage)
+ else
{
- case SelectionStageWaitSize:
+ switch (lastClientStage)
{
- PrintClientSelectionStage();
- #ifdef DEBUG
- fprintf (stderr, "%s: Got size notify event for client [%d].\n", __func__,
- lastClientClientPtr -> index);
- #endif
-
- if (ulReturnBytesLeft == 0)
+ case SelectionStageWaitSize:
{
+ PrintClientSelectionStage();
#ifdef DEBUG
- fprintf (stderr, "%s: Aborting selection notify procedure for client [%d].\n", __func__,
- lastClientClientPtr -> index);
+ fprintf (stderr, "%s: Got size notify event for client [%d].\n", __func__,
+ CLINDEX(lastClientClientPtr));
#endif
- nxagentSendSelectionNotify(None);
+ if (ulReturnBytesLeft == 0)
+ {
+ #ifdef DEBUG
+ fprintf (stderr, "%s: Aborting selection notify procedure.\n", __func__);
+ #endif
- lastClientWindowPtr = NULL;
- SetClientSelectionStage(None);
+ endTransfer(SELECTION_FAULT);
+ }
+ else
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Got property size from remote server.\n", __func__);
+ #endif
- SAFE_XFree(pszReturnData);
- return;
- }
+ /*
+ * Request the selection data now.
+ */
+ lastClientPropertySize = ulReturnBytesLeft;
+ SetClientSelectionStage(QueryData);
- #ifdef DEBUG
- fprintf(stderr, "%s: Got property size from remote server.\n", __func__);
- #endif
+ nxagentTransferSelection(resource);
+ }
+ break;
+ }
+ case SelectionStageWaitData:
+ {
+ PrintClientSelectionStage();
+ #ifdef DEBUG
+ fprintf (stderr, "%s: Got data notify event for client [%d].\n", __func__,
+ CLINDEX(lastClientClientPtr));
+ #endif
- /*
- * Request the selection data now.
- */
+ if (ulReturnBytesLeft != 0)
+ {
+ #ifdef DEBUG
+ fprintf (stderr, "%s: Aborting selection notify procedure.\n", __func__);
+ #endif
- lastClientPropertySize = ulReturnBytesLeft;
- SetClientSelectionStage(QueryData);
+ endTransfer(SELECTION_FAULT);
+ }
+ else
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Got property content from remote server.\n", __func__);
+ #endif
- nxagentTransferSelection(resource);
+ ChangeWindowProperty(lastClientWindowPtr,
+ lastClientProperty,
+ lastClientTarget,
+ resultFormat, PropModeReplace,
+ ulReturnItems, pszReturnData, 1);
- break;
- }
- case SelectionStageWaitData:
- {
- PrintClientSelectionStage();
- #ifdef DEBUG
- fprintf (stderr, "%s: Got data notify event for client [%d].\n", __func__,
- lastClientClientPtr -> index);
- #endif
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Selection property [%d][%s] changed to [\"%*.*s\"...]\n", __func__,
+ lastClientProperty, validateString(NameForAtom(lastClientProperty)),
+ (int)(min(20, ulReturnItems * resultFormat / 8)),
+ (int)(min(20, ulReturnItems * resultFormat / 8)),
+ pszReturnData);
+ #endif
- if (ulReturnBytesLeft != 0)
+ endTransfer(SELECTION_SUCCESS);
+ }
+ break;
+ }
+ default:
{
#ifdef DEBUG
- fprintf (stderr, "%s: Aborting selection notify procedure for client [%d].\n", __func__,
- lastClientClientPtr -> index);
+ fprintf (stderr, "%s: WARNING! Inconsistent state [%s] for client [%d].\n", __func__,
+ GetClientSelectionStageString(lastClientStage), CLINDEX(lastClientClientPtr));
#endif
-
- nxagentSendSelectionNotify(None);
-
- lastClientWindowPtr = NULL;
- SetClientSelectionStage(None);
-
- SAFE_XFree(pszReturnData);
- return;
+ break;
}
-
- #ifdef DEBUG
- fprintf(stderr, "%s: Got property content from remote server.\n", __func__);
- #endif
-
- ChangeWindowProperty(lastClientWindowPtr,
- lastClientProperty,
- lastClientTarget,
- resultFormat, PropModeReplace,
- ulReturnItems, pszReturnData, 1);
-
- #ifdef DEBUG
- fprintf(stderr, "%s: Selection property [%s] changed to [%s]\n", __func__,
- validateString(NameForAtom(lastClientProperty)), pszReturnData);
- #endif
-
- nxagentSendSelectionNotify(lastClientProperty);
-
- /*
- * Enable further requests from clients.
- */
-
- lastClientWindowPtr = NULL;
- SetClientSelectionStage(None);
-
- break;
- }
- default:
- {
- #ifdef DEBUG
- fprintf (stderr, "%s: WARNING! Inconsistent state [%s] for client [%d].\n", __func__,
- GetClientSelectionStageString(lastClientStage), lastClientClientPtr -> index);
- #endif
-
- break;
}
}
-
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)
{
return;
}
#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
PrintClientSelectionStage();
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))
{
#ifdef DEBUG
fprintf(stderr, "%s: Starting selection transferral for client [%d].\n", __func__,
- lastClientClientPtr -> index);
+ CLINDEX(lastClientClientPtr));
#endif
/*
@@ -1085,31 +1259,21 @@ void nxagentNotifySelection(XEvent *X)
{
#ifdef DEBUG
fprintf(stderr, "%s: WARNING! Resetting selection transferral for client [%d].\n", __func__,
- lastClientClientPtr -> index);
+ CLINDEX(lastClientClientPtr));
#endif
- nxagentSendSelectionNotify(None);
-
- lastClientWindowPtr = NULL;
- SetClientSelectionStage(None);
+ endTransfer(SELECTION_FAULT);
}
-
- return;
}
else
{
- int i = 0;
-
- while ((i < nxagentMaxSelections) && (lastSelectionOwner[i].selection != X->xselection.selection))
- {
- i++;
- }
-
+ int i = nxagentFindLastSelectionOwnerIndex(X->xselection.selection);
if (i < nxagentMaxSelections)
{
+ /* if the last owner was an internal one */
if ((lastSelectionOwner[i].client != NULL) &&
(lastSelectionOwner[i].windowPtr != NULL) &&
- (X->xselection.property == clientCutProperty))
+ (X->xselection.property == serverClientCutProperty))
{
Atom atomReturnType;
int resultFormat;
@@ -1117,94 +1281,97 @@ void nxagentNotifySelection(XEvent *X)
unsigned long ulReturnBytesLeft;
unsigned char *pszReturnData = NULL;
+ /* first get size values ... */
int result = GetWindowProperty(lastSelectionOwner[i].windowPtr, clientCutProperty, 0, 0, False,
AnyPropertyType, &atomReturnType, &resultFormat,
&ulReturnItems, &ulReturnBytesLeft, &pszReturnData);
#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
if (result == BadAlloc || result == BadAtom ||
result == BadWindow || result == BadValue)
{
- fprintf (stderr, "Client GetProperty failed. Error = %s", GetXErrorString(result));
lastServerProperty = None;
}
else
{
+ /* ... then use the size values for the actual request */
result = GetWindowProperty(lastSelectionOwner[i].windowPtr, clientCutProperty, 0,
ulReturnBytesLeft, False, AnyPropertyType, &atomReturnType,
&resultFormat, &ulReturnItems, &ulReturnBytesLeft,
&pszReturnData);
#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
if (result == BadAlloc || result == BadAtom ||
result == BadWindow || result == BadValue)
{
- fprintf (stderr, "SelectionNotify - XChangeProperty failed. Error = %s\n", GetXErrorString(result));
lastServerProperty = None;
}
else
{
- result = XChangeProperty(nxagentDisplay,
- lastServerRequestor,
- lastServerProperty,
- lastServerTarget,
- 8,
- PropModeReplace,
- pszReturnData,
- ulReturnItems);
+ /* 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,
+ lastServerProperty,
+ lastServerTarget,
+ 8,
+ PropModeReplace,
+ pszReturnData,
+ ulReturnItems);
+
+ #ifdef DEBUG
+ {
+ 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
}
- #ifdef DEBUG
- fprintf(stderr, "%s: XChangeProperty() returned [%s]\n", __func__, GetXErrorString(result));
- #endif
+ /* FIXME: free it or not? */
/*
* 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.
*/
-
- eventSelection.target = lastServerTarget;
- eventSelection.property = lastServerProperty;
- eventSelection.time = lastServerTime;
-
- /*
- * eventSelection.time = CurrentTime;
- * eventSelection.time = lastServerTime;
- */
-
+ XSelectionEvent eventSelection = {
+ .requestor = lastServerRequestor,
+ .selection = X->xselection.selection,
+ /* .target = X->xselection.target, */
+ .target = lastServerTarget,
+ .property = lastServerProperty,
+ .time = lastServerTime,
+ /* .time = CurrentTime */
+ };
#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
- result = XSendEvent(nxagentDisplay,
- 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");
- }
+ SendSelectionNotifyEventToServer(&eventSelection);
lastServerRequestor = None; /* allow further request */
}
@@ -1213,21 +1380,24 @@ void nxagentNotifySelection(XEvent *X)
}
/*
- * Acquire selection so we don't get selection
- * requests from real X clients.
+ * Let nxagent's serverWindow acquire the selection. All requests from
+ * 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)
{
- int i;
-
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__,
lastServerRequestor);
#endif
+ /* FIXME: maybe we should put back the event that lead us here. */
return;
}
@@ -1235,39 +1405,44 @@ void nxagentResetSelectionOwner(void)
* 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);
#ifdef DEBUG
- fprintf(stderr, "%s: Reset clipboard state.\n", __func__);
+ fprintf(stderr, "%s: Reset selection state for selection [%d].\n", __func__, i);
#endif
- lastSelectionOwner[i].client = NULL;
- lastSelectionOwner[i].window = None;
+ nxagentClearSelectionOwner(&lastSelectionOwner[i]);
lastSelectionOwner[i].windowPtr = NULL;
- lastSelectionOwner[i].lastTimeChanged = GetTimeInMillis();
}
lastClientWindowPtr = NULL;
SetClientSelectionStage(None);
+ /* Hmm, this is already None when reaching this */
lastServerRequestor = None;
-
- return;
}
#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 *args)
{
/*
- * Only act if the Trap is unset. The trap indicates that we are
- * triggered by a clipboard event originating from the real X
- * server. In that case we do not want to propagate back changes to
- * the real X server, because it already knows about them and we
+ * Only act if the trap is unset. The trap indicates that we are
+ * triggered by an XFixes clipboard event originating from the real
+ * X server. In that case we do not want to propagate back changes
+ * 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
- * way to identify that situation during Callback processing we
+ * way to identify that situation during callback processing we
* could get rid of the Trap...
*/
if (nxagentExternalClipboardEventTrap != 0)
@@ -1290,7 +1465,7 @@ void nxagentSetSelectionCallback(CallbackListPtr *callbacks, void *data,
{
#ifdef DEBUG
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));
#endif
@@ -1326,12 +1501,12 @@ void nxagentSetSelectionCallback(CallbackListPtr *callbacks, void *data,
}
#endif
+/*
+ * This is called from the nxagentSetSelectionCallback, so it is using
+ * internal Atoms
+ */
void nxagentSetSelectionOwner(Selection *pSelection)
{
- #ifdef DEBUG
- fprintf(stderr, "%s: Got called.\n", __func__);
- #endif
-
if (agentClipboardStatus != 1)
{
return;
@@ -1342,10 +1517,14 @@ void nxagentSetSelectionOwner(Selection *pSelection)
serverWindow);
#endif
- #ifdef TEST
+ #if defined(TEST) || defined(DEBUG)
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);
}
#endif
@@ -1356,28 +1535,37 @@ void nxagentSetSelectionOwner(Selection *pSelection)
for (int i = 0; i < nxagentMaxSelections; i++)
{
+ /* FIXME: using CurrentSelections with the index limited my MaxSelections looks wrong */
if (pSelection->selection == CurrentSelections[i].selection)
{
#ifdef DEBUG
- fprintf(stderr, "%s: lastSelectionOwner.client [0x%x] -> [0x%x]\n", __func__, lastSelectionOwner[i].client, pSelection->client);
- fprintf(stderr, "%s: lastSelectionOwner.window [0x%x] -> [0x%x]\n", __func__, lastSelectionOwner[i].window, pSelection->window);
- 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);
- fprintf(stderr, "%s: lastSelectionOwner.lastTimeChanged [%d]\n", __func__, lastSelectionOwner[i].lastTimeChanged);
+ fprintf(stderr, "%s: lastSelectionOwner.client [%p] index [%d] -> [%p] index [%d]\n", __func__,
+ (void *)lastSelectionOwner[i].client,
+ CLINDEX(lastSelectionOwner[i].client),
+ (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
/*
* inform the real X server that our serverWindow is the
- * clipboard owner. The real owner window (inside nxagent) is
- * stored in lastSelectionOwner.window.
- * lastSelectionOwner.windowPtr points to the struct that
- * contains all information about the owner window
+ * clipboard owner.
*/
XSetSelectionOwner(nxagentDisplay, lastSelectionOwner[i].selection, serverWindow, CurrentTime);
- lastSelectionOwner[i].client = pSelection->client;
- lastSelectionOwner[i].window = pSelection->window;
- lastSelectionOwner[i].windowPtr = pSelection->pWin;
- lastSelectionOwner[i].lastTimeChanged = GetTimeInMillis();
+ /*
+ * The real owner window (inside nxagent) is stored in
+ * lastSelectionOwner.window. lastSelectionOwner.windowPtr
+ * points to the struct that contains all information about the
+ * owner window.
+ */
+ nxagentStoreSelectionOwner(&lastSelectionOwner[i], pSelection);
}
}
@@ -1410,12 +1598,9 @@ FIXME
void nxagentNotifyConvertFailure(ClientPtr client, Window requestor,
Atom selection, Atom target, Time time)
{
- xEvent x;
-
-/*
-FIXME: Why this pointer can be not a valid
- client pointer?
-*/
+ /*
+ * Check if the client is still valid.
+ */
if (clients[client -> index] != client)
{
#ifdef WARNING
@@ -1425,43 +1610,33 @@ FIXME: Why this pointer can be not a valid
return;
}
- 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);
+ SendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
}
+/*
+ * 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,
Window requestor, Atom property, Atom target, Time time)
{
- const char *strTarget;
- int i;
-
if (agentClipboardStatus != 1 ||
nxagentOption(Clipboard) == ClipboardServer)
{
return 0;
}
- /*
- * 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++)
+ for (int i = 0; i < nxagentMaxSelections; i++)
{
if ((selection == CurrentSelections[i].selection) &&
(lastSelectionOwner[i].client != NULL))
{
+ /*
+ * There is a client owner on the agent side, let normal dix stuff happen.
+ */
return 0;
}
}
@@ -1498,111 +1673,118 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
}
}
- #ifdef TEST
- fprintf(stderr, "%s: client [%d] ask for sel [%s] "
- "on window [%x] prop [%s] target [%s].\n", __func__,
- client -> index, validateString(NameForAtom(selection)), requestor,
- validateString(NameForAtom(property)), validateString(NameForAtom(target)));
+ #if defined(TEST) || defined(DEBUG)
+ fprintf(stderr, "%s: client [%d] requests sel [%s] "
+ "on window [%x] prop [%d][%s] target [%d][%s].\n", __func__,
+ CLINDEX(client), validateString(NameForAtom(selection)), requestor,
+ property, validateString(NameForAtom(property)),
+ target, validateString(NameForAtom(target)));
#endif
- strTarget = NameForAtom(target);
+ const char *strTarget = NameForAtom(target);
if (strTarget == NULL)
{
+ #ifdef DEBUG
+ fprintf(stderr, "%s: cannot find name for target Atom [%d] - returning\n", __func__, target);
+ #endif
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)
{
- Atom xa_STRING[4];
- xEvent x;
-
/* --- Order changed by dimbor (prevent sending COMPOUND_TEXT to client --- */
- xa_STRING[0] = XA_STRING;
- xa_STRING[1] = clientUTF8_STRING;
- xa_STRING[2] = clientTEXT;
- xa_STRING[3] = clientCOMPOUND_TEXT;
+ Atom targets[] = {XA_STRING, clientUTF8_STRING, clientTEXT, clientCOMPOUND_TEXT};
+ int numTargets = 4;
+
+ #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,
property,
MakeAtom("ATOM", 4, 1),
sizeof(Atom)*8,
PropModeReplace,
- 4,
- &xa_STRING, 1);
+ numTargets,
+ &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,
- NoEventMask , NullGrab);
+ SendSelectionNotifyEventToClient(client, time, requestor, selection, target, property);
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))
{
- int i = 0;
-
- while ((i < NumCurrentSelections) &&
- CurrentSelections[i].selection != selection) i++;
-
+ int i = nxagentFindCurrentSelectionIndex(selection);
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,
property,
- target,
+ XA_INTEGER,
32,
PropModeReplace,
1,
(unsigned char *) &lastSelectionOwner[i].lastTimeChanged,
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,
- NoEventMask , NullGrab);
+ SendSelectionNotifyEventToClient(client, time, requestor, selection, target, property);
return 1;
-
}
}
+ #ifdef DEBUG
if (lastClientClientPtr == client && (GetTimeInMillis() - lastClientReqTime < 5000))
{
/*
- * The same client made consecutive requests
- * of clipboard contents with less than 5
- * seconds time interval between them.
+ * The same client made consecutive requests of clipboard content
+ * with less than 5 seconds time interval between them.
*/
- #ifdef DEBUG
fprintf(stderr, "%s: Consecutives request from client [%p] selection [%u] "
"elapsed time [%u] clientAccum [%d]\n", __func__, (void *) client, selection,
GetTimeInMillis() - lastClientReqTime, clientAccum);
- #endif
clientAccum++;
}
else
{
+ /* reset clientAccum as now another client requested the clipboard content */
if (lastClientClientPtr != client)
{
clientAccum = 0;
}
}
+ #endif
if ((target == clientTEXT) ||
(target == XA_STRING) ||
@@ -1611,6 +1793,10 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
{
lastClientWindowPtr = pWin;
SetClientSelectionStage(None);
+ /*
+ * store the original requestor, we need that later after
+ * serverCutProperty contains the desired selection content
+ */
lastClientRequestor = requestor;
lastClientClientPtr = client;
lastClientTime = time;
@@ -1618,25 +1804,41 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
lastClientSelection = selection;
lastClientTarget = target;
- lastClientReqTime = (GetTimeInMillis() - lastClientReqTime) > 5000 ?
- GetTimeInMillis() : lastClientReqTime;
+ /* if the last client request time is more than 5s ago update it. Why? */
+ if ((GetTimeInMillis() - lastClientReqTime) > 5000)
+ lastClientReqTime = GetTimeInMillis();
if (selection == MakeAtom("CLIPBOARD", 9, 0))
{
selection = lastSelectionOwner[nxagentClipboardSelection].selection;
}
+ /*
+ * we only convert to either UTF8 or XA_STRING, despite accepting
+ * TEXT and COMPOUND_TEXT.
+ */
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,
serverWindow, CurrentTime);
}
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,
serverWindow, CurrentTime);
}
+ /* FIXME: check returncode of XConvertSelection */
+
#ifdef DEBUG
fprintf(stderr, "%s: Sent XConvertSelection with target=[%s], property [%s]\n", __func__,
validateString(NameForAtom(target)), validateString(NameForAtom(property)));
@@ -1646,26 +1848,32 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
}
else
{
- xEvent x;
-
- #ifdef DEBUG
- fprintf(stderr, "%s: Xserver generates a SelectionNotify event "
- "to the requestor with property None.\n", __func__);
- #endif
+ /* deny request */
+ SendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
- 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 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)
{
#ifdef DEBUG
@@ -1681,56 +1889,92 @@ int nxagentSendNotify(xEvent *event)
}
#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
- 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.
+ *
+ * .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));
- x.type = SelectionNotify;
- x.send_event = True;
- x.display = nxagentDisplay;
- x.requestor = serverWindow;
+ XSelectionEvent eventSelection = {
+ .requestor = serverWindow,
+ .selection = event->u.selectionNotify.selection,
+ .target = event->u.selectionNotify.target,
+ .property = serverClientCutProperty,
+ .time = CurrentTime,
+ };
/*
- * On real server, the right CLIPBOARD atom is
- * XInternAtom(nxagentDisplay, "CLIPBOARD", 1).
+ * On the real server, the right CLIPBOARD atom is
+ * 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))
{
- 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
{
- 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
- 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
- result = XSendEvent (nxagentDisplay, x.requestor, False,
- 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__);
- }
+ SendSelectionNotifyEventToServer(&eventSelection);
return 1;
}
@@ -1740,43 +1984,38 @@ int nxagentSendNotify(xEvent *event)
return 0;
}
-WindowPtr nxagentGetClipboardWindow(Atom property, WindowPtr pWin)
+WindowPtr nxagentGetClipboardWindow(Atom property)
{
- int i = 0;
-
- #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++;
- }
+ int i = nxagentFindLastSelectionOwnerIndex(nxagentLastRequestedSelection);
if ((i < nxagentMaxSelections) && (property == clientCutProperty) &&
(lastSelectionOwner[i].windowPtr != NULL))
{
#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
return lastSelectionOwner[i].windowPtr;
}
else
{
- #ifdef DEBUG
- fprintf(stderr, "%s: Returning original target window [%p].\n", __func__, (void *)pWin);
- #endif
-
- return pWin;
+ return NULL;
}
+}
+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 i;
Window iWindow = nxagentWindow(pWin);
#ifdef DEBUG
@@ -1792,20 +2031,10 @@ int nxagentInitClipboard(WindowPtr pWin)
FatalError("nxagentInitClipboard: Failed to allocate memory for the clipboard selections.\n");
}
- nxagentClipboardAtom = nxagentAtoms[10]; /* CLIPBOARD */
- nxagentTimestampAtom = nxagentAtoms[11]; /* TIMESTAMP */
-
- lastSelectionOwner[nxagentPrimarySelection].selection = XA_PRIMARY;
- lastSelectionOwner[nxagentPrimarySelection].client = NullClient;
- lastSelectionOwner[nxagentPrimarySelection].window = screenInfo.screens[0]->root->drawable.id;
- lastSelectionOwner[nxagentPrimarySelection].windowPtr = NULL;
- lastSelectionOwner[nxagentPrimarySelection].lastTimeChanged = GetTimeInMillis();
+ serverTIMESTAMP = nxagentAtoms[11]; /* TIMESTAMP */
- 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();
+ nxagentInitSelectionOwner(&lastSelectionOwner[nxagentPrimarySelection], XA_PRIMARY);
+ nxagentInitSelectionOwner(&lastSelectionOwner[nxagentClipboardSelection], nxagentAtoms[10]); /* CLIPBOARD */
#ifdef NXAGENT_TIMESTAMP
{
@@ -1827,6 +2056,8 @@ int nxagentInitClipboard(WindowPtr pWin)
serverTARGETS = nxagentAtoms[6]; /* TARGETS */
serverTEXT = nxagentAtoms[7]; /* TEXT */
serverUTF8_STRING = nxagentAtoms[12]; /* UTF8_STRING */
+ /* see nxagentSendNotify for an explanation */
+ serverClientCutProperty = nxagentAtoms[15]; /* NX_CUT_BUFFER_CLIENT */
if (serverCutProperty == None)
{
@@ -1858,7 +2089,7 @@ int nxagentInitClipboard(WindowPtr pWin)
fprintf(stderr, "%s: Registering for XFixesSelectionNotify events.\n", __func__);
#endif
- for (i = 0; i < nxagentMaxSelections; i++)
+ for (int i = 0; i < nxagentMaxSelections; i++)
{
XFixesSelectSelectionInput(nxagentDisplay, iWindow,
lastSelectionOwner[i].selection,
@@ -1898,7 +2129,7 @@ int nxagentInitClipboard(WindowPtr pWin)
* 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)
{
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Clipboard.h b/nx-X11/programs/Xserver/hw/nxagent/Clipboard.h
index 62fc32fd9..c2e783cb9 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Clipboard.h
+++ b/nx-X11/programs/Xserver/hw/nxagent/Clipboard.h
@@ -64,6 +64,7 @@ extern int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom select
void nxagentClearSelection();
void nxagentRequestSelection();
-void nxagentNotifySelection();
+void nxagentHandleSelectionNotifyFromXServer();
+int nxagentFindCurrentSelectionIndex(Atom sel);
#endif /* __Clipboard_H__ */
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Events.c b/nx-X11/programs/Xserver/hw/nxagent/Events.c
index e9de1450d..cbdf17a22 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Events.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Events.c
@@ -944,7 +944,7 @@ void nxagentDispatchEvents(PredicateFuncPtr predicate)
fprintf(stderr, "nxagentDispatchEvents: Going to handle new SelectionNotify event.\n");
#endif
- nxagentNotifySelection(&X);
+ nxagentHandleSelectionNotifyFromXServer(&X);
break;
}
@@ -2933,12 +2933,7 @@ int nxagentHandleXFixesSelectionNotify(XEvent *X)
if (SelectionCallback)
{
- int i = 0;
-
- while ((i < NumCurrentSelections) &&
- CurrentSelections[i].selection != local)
- i++;
-
+ int i = nxagentFindCurrentSelectionIndex(local);
if (i < NumCurrentSelections)
{
SelectionInfoRec info;
diff --git a/nx-X11/programs/Xserver/hw/nxagent/NXdispatch.c b/nx-X11/programs/Xserver/hw/nxagent/NXdispatch.c
index b7a054913..4a9ae73ae 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/NXdispatch.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/NXdispatch.c
@@ -691,11 +691,7 @@ ProcConvertSelection(register ClientPtr client)
(stuff->selection == MakeAtom("CLIPBOARD", 9, 0))) &&
nxagentOption(Clipboard) != ClipboardNone)
{
- int i = 0;
-
- while ((i < NumCurrentSelections) &&
- CurrentSelections[i].selection != stuff->selection) i++;
-
+ int i = nxagentFindCurrentSelectionIndex(stuff->selection);
if ((i < NumCurrentSelections) && (CurrentSelections[i].window != None))
{
if (nxagentConvertSelection(client, pWin, stuff->selection, stuff->requestor,