aboutsummaryrefslogtreecommitdiff
path: root/nx-X11
diff options
context:
space:
mode:
authorMike Gabriel <mike.gabriel@das-netzwerkteam.de>2021-10-18 09:28:50 +0200
committerMike Gabriel <mike.gabriel@das-netzwerkteam.de>2021-10-18 09:28:50 +0200
commitb7034724abb859db36a3fa4312e4827141114f45 (patch)
tree5e4406bd491e61be781de5b2075dabd76ba9514b /nx-X11
parentb67b910a6adf3287fad10cc97cf9484b0057b0bd (diff)
parent31057d00b8cdbe97837f508c3cb0f2c521a30266 (diff)
downloadnx-libs-b7034724abb859db36a3fa4312e4827141114f45.tar.gz
nx-libs-b7034724abb859db36a3fa4312e4827141114f45.tar.bz2
nx-libs-b7034724abb859db36a3fa4312e4827141114f45.zip
Merge branch 'uli42-pr/many_clipboard_fixes' into 3.6.x
Attributes GH PR #1018: https://github.com/ArcticaProject/nx-libs/pull/1018
Diffstat (limited to 'nx-X11')
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Args.c19
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Atoms.c71
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Atoms.h18
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Clipboard.c3059
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Clipboard.h7
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Events.c119
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/NXdispatch.c15
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/NXevents.c7
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Options.c1
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Options.h7
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Trap.c8
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/Trap.h7
-rw-r--r--nx-X11/programs/Xserver/hw/nxagent/man/nxagent.113
13 files changed, 2264 insertions, 1087 deletions
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Args.c b/nx-X11/programs/Xserver/hw/nxagent/Args.c
index be13efacd..ea17e4813 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Args.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Args.c
@@ -972,6 +972,12 @@ int ddxProcessArgument(int argc, char *argv[], int i)
return 2;
}
+ if (!strcmp(argv[i], "-textclipboard"))
+ {
+ nxagentChangeOption(TextClipboard, True);
+ return 1;
+ }
+
if (!strcmp(argv[i], "-bs"))
{
nxagentChangeOption(BackingStore, BackingStoreNever);
@@ -1487,6 +1493,18 @@ static void nxagentParseSingleOption(char *name, char *value)
}
return;
}
+ else if (!strcmp(name, "textclipboard"))
+ {
+ if (!strcmp(value, "0"))
+ {
+ nxagentChangeOption(TextClipboard, False);
+ }
+ else
+ {
+ nxagentChangeOption(TextClipboard, True);
+ }
+ return;
+ }
else
{
#ifdef DEBUG
@@ -2129,6 +2147,7 @@ void ddxUseMsg(void)
ErrorF("-nokbreset don't reset keyboard device if the session is resumed\n");
ErrorF("-noxkblock always allow applications to change layout through XKEYBOARD\n");
ErrorF("-autograb enable autograb\n");
+ ErrorF("-textclipboard limit clipboard data to text only\n");
ErrorF("-irlimit maximum image data rate to the encoder input in kB/s.\n");
ErrorF("-tile WxH maximum size of image tiles (minimum allowed: 32x32)\n");
ErrorF("-keystrokefile file file with keyboard shortcut definitions\n");
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Atoms.c b/nx-X11/programs/Xserver/hw/nxagent/Atoms.c
index 2ddf87e55..6ce8b21e8 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Atoms.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Atoms.c
@@ -76,7 +76,7 @@ Atom nxagentAtoms[NXAGENT_NUMBER_OF_ATOMS];
* Careful! Do not change indices here! Some of those are referenced
* at other places via nxagentAtoms[index].
*/
-static char *nxagentAtomNames[NXAGENT_NUMBER_OF_ATOMS + 1] =
+static char *nxagentAtomNames[NXAGENT_NUMBER_OF_ATOMS + 2] =
{
"NX_IDENTITY", /* 0 */
/* NX_IDENTITY was used in earlier nx versions to communicate
@@ -135,6 +135,27 @@ static char *nxagentAtomNames[NXAGENT_NUMBER_OF_ATOMS + 1] =
"COMPOUND_TEXT", /* 16 */
/* one of the supported data formats for selections. Standard
ICCCM Atom */
+ "INCR", /* 17 */
+ /* incremental clipboard transfers. Standard
+ ICCCM Atom */
+ "MULTIPLE", /* 18 */
+ /* request selection in multiple formats at once. Standard
+ ICCCM Atom */
+ "DELETE", /* 19 */
+ /* request to delete selection. Standard ICCCM Atom */
+ "INSERT_SELECTION", /* 20 */
+ /* request to insert other selection. Standard ICCCM Atom */
+ "INSERT_PROPERTY", /* 21 */
+ /* request to insert content of property into selection. Standard
+ ICCCM Atom */
+ "SAVE_TARGETS", /* 22 */
+ /* request to save clipboard content to clipboard manager on
+ exit, see
+ https://www.freedesktop.org/wiki/ClipboardManager */
+ "TARGET_SIZES", /* 23 */
+ /* request to retrieve the sizes of the clipboard content in
+ various formats, see
+ https://www.freedesktop.org/wiki/ClipboardManager */
NULL,
NULL
};
@@ -750,6 +771,54 @@ XlibAtom nxagentLocalToRemoteAtom(Atom local)
}
}
+/*
+ * This is mainly used to simplify debug prints. It returns
+ * the string for a remote atom or NULL if atom is unknown/invalid
+ *
+ * The string must NOT be freed by the caller.
+ */
+const char *nxagentRemoteAtomToString(XlibAtom remote)
+{
+ if (remote == None || remote == BAD_RESOURCE)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: remote [%d] is None or BAD_RESOURCE\n", __func__, remote);
+ #endif
+ return NULL;
+ }
+
+ /* no mapping required for built-in atoms */
+ if (remote <= XA_LAST_PREDEFINED)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: remote [%d] is <= XA_LAST_PREDEFINED [%d]\n", __func__, remote, XA_LAST_PREDEFINED);
+ #endif
+
+ /* simply use the builtin string that is the same on every X server */
+ return NameForAtom(remote);
+ }
+
+ AtomMap *current = nxagentFindAtomByRemoteValue(remote);
+ if (current)
+ {
+ return current->string;
+ }
+ else
+ {
+ /* fill up the cache */
+ Atom local = nxagentRemoteToLocalAtom(remote);
+ if (local != None)
+ {
+ current = nxagentFindAtomByRemoteValue(remote);
+ if (current)
+ {
+ return current->string;
+ }
+ }
+ }
+ return NULL;
+}
+
Atom nxagentRemoteToLocalAtom(XlibAtom remote)
{
if (remote == None || remote == BAD_RESOURCE)
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Atoms.h b/nx-X11/programs/Xserver/hw/nxagent/Atoms.h
index 9006b2253..a9f730d76 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 18
+#define NXAGENT_NUMBER_OF_ATOMS 24
extern Atom nxagentAtoms[NXAGENT_NUMBER_OF_ATOMS];
@@ -71,12 +71,24 @@ void nxagentWMDetect(void);
XlibAtom nxagentMakeAtom(char *, unsigned, Bool);
/*
- * Converts local atoms in remote atoms and viceversa.
+ * Converts local atoms to remote atoms and viceversa.
*/
Atom nxagentRemoteToLocalAtom(XlibAtom);
XlibAtom nxagentLocalToRemoteAtom(Atom);
-#endif
+/*
+ * return the string belonging to an atom. String MUST NOT
+ * be freed by the caller!
+ */
+const char *nxagentRemoteAtomToString(XlibAtom remote);
+
+/*
+ * supply two macros that also validate the output.
+ */
+#define NameForLocalAtom(_atom) validateString(NameForAtom(_atom))
+#define NameForRemoteAtom(_xlibatom) validateString(nxagentRemoteAtomToString(_xlibatom))
+
+#endif /* XlibAtom */
#endif /* __Atoms_H__ */
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Clipboard.c b/nx-X11/programs/Xserver/hw/nxagent/Clipboard.c
index 22fc96f4f..72ca6ac64 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Clipboard.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Clipboard.c
@@ -60,50 +60,88 @@
#undef DEBUG
/*
+ * Define this to see the clipboard content in the debug output. As
+ * this can lead to information leaking it must be activated
+ * explicitly!
+ */
+#undef PRINT_CLIPBOARD_CONTENT_ON_DEBUG
+
+/*
+ * Define these to also support special targets TEXT and COMPOUND_TEXT
+ * in text-only mode. We do not have a special handling for these. See
+ * https://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html#text_properties
+ * for details.
+ */
+#undef SUPPORT_TEXT_TARGET
+#undef SUPPORT_COMPOUND_TEXT_TARGET
+
+/*
* These are defined in the dispatcher.
*/
extern int NumCurrentSelections;
extern Selection *CurrentSelections;
-int nxagentLastClipboardClient = -1;
-
static int agentClipboardInitialized = False;
static int clientAccum;
XlibAtom serverTransToAgentProperty;
Atom clientCutProperty;
-static Window serverWindow;
+static XlibWindow serverWindow;
const int nxagentPrimarySelection = 0;
const int nxagentClipboardSelection = 1;
const int nxagentMaxSelections = 2;
+/* store the remote atom for all selections */
+static XlibAtom *remoteSelectionAtoms = NULL;
+static Atom *localSelelectionAtoms = NULL;
+
+/*
+ * The real owner window (inside nxagent) is stored in
+ * lastSelectionOwner[index].window.
+ * lastSelectionOwner[index].windowPtr points to the struct that
+ * contains all information about the owner window.
+ * lastTimeChanged is always a local time.
+ */
typedef struct _SelectionOwner
{
- XlibAtom selection; /* _external_ Atom */
- ClientPtr client; /* internal client */
- Window window; /* internal window id */
- WindowPtr windowPtr; /* internal window struct */
- Time lastTimeChanged; /* internal time */
+ ClientPtr client; /* local client */
+ Window window; /* local window id */
+ WindowPtr windowPtr; /* local window struct */
+ Time lastTimeChanged; /* local time (server 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
+ * This contains the last selection owner for each selection. If
+ * .client is NULL the owner is outside nxagent or there is no owner.
*/
-static SelectionOwner *lastSelectionOwner;
-static XlibAtom serverLastRequestedSelection;
-#define IS_INTERNAL_OWNER(lsoindex) (lastSelectionOwner[lsoindex].client != NULL)
+static SelectionOwner *lastSelectionOwner = NULL;
/*
- * Needed to handle the notify selection event to
- * be sent to client once the selection property
- * has been retrieved from the real X server.
- */
+ * Cache for targets the current selection owner
+ * has to offer. We are storing the targets
+ * after they have been converted.
+*/
+typedef struct _Targets
+{
+ Bool type; /* EMPTY, FOR_LOCAL, FOR_REMOTE */
+ unsigned int numTargets;
+ Atom *forLocal; /* Atoms converted for local -> type Atom, not XlibAtom */
+ XlibAtom *forRemote; /* Atoms converted for remote -> type XlibAtom, not Atom */
+} Targets;
+
+#define EMPTY 0
+#define FOR_REMOTE 1
+#define FOR_LOCAL 2
+
+static Targets *targetCache = NULL;
+
+/* FIXME: can this also be stored per selection? */
+static XlibAtom serverLastRequestedSelection = -1;
+
+#define IS_LOCAL_OWNER(lsoindex) (lastSelectionOwner[lsoindex].client != NullClient)
typedef enum
{
@@ -114,45 +152,95 @@ typedef enum
SelectionStageWaitData
} ClientSelectionStage;
-static WindowPtr lastClientWindowPtr;
-static ClientPtr lastClientClientPtr;
-static Window lastClientRequestor;
-static Atom lastClientProperty;
-static Atom lastClientSelection;
-static Atom lastClientTarget;
-static Time lastClientTime;
-static Time lastClientReqTime;
-static unsigned long lastClientPropertySize;
-
-static ClientSelectionStage lastClientStage;
+/*
+ * Needed to handle the notify selection event to be sent to the
+ * waiting client once the selection property has been retrieved from
+ * the real X server.
+ */
-static Window lastServerRequestor;
-static XlibAtom lastServerProperty;
-static XlibAtom lastServerTarget;
-static Time lastServerTime;
+typedef struct _lastClient
+{
+ WindowPtr windowPtr;
+ ClientPtr clientPtr;
+ Window requestor;
+ Atom property;
+ Atom target;
+ Time time;
+ Time reqTime;
+ unsigned long propertySize;
+ ClientSelectionStage stage;
+ int resource; /* nxcompext resource where collected property data is stored */
+} lastClient;
+
+static lastClient *lastClients;
+
+typedef struct _lastServer {
+ XlibWindow requestor;
+ XlibAtom property;
+ XlibAtom target;
+ Time time;
+} lastServer;
+
+static lastServer *lastServers;
+/*
+ * FIXME: use (additional) Atoms.c helpers to get rid of all these
+ * Atoms and strings
+ */
static XlibAtom serverTARGETS;
static XlibAtom serverTIMESTAMP;
+static XlibAtom serverINCR;
+static XlibAtom serverMULTIPLE;
+static XlibAtom serverDELETE;
+static XlibAtom serverINSERT_SELECTION;
+static XlibAtom serverINSERT_PROPERTY;
+static XlibAtom serverSAVE_TARGETS;
+static XlibAtom serverTARGET_SIZES;
+#ifdef SUPPORT_TEXT_TARGET
static XlibAtom serverTEXT;
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
static XlibAtom serverCOMPOUND_TEXT;
+#endif
static XlibAtom serverUTF8_STRING;
static XlibAtom serverTransFromAgentProperty;
static Atom clientTARGETS;
static Atom clientTIMESTAMP;
+static Atom clientINCR;
+static Atom clientMULTIPLE;
+static Atom clientDELETE;
+static Atom clientINSERT_SELECTION;
+static Atom clientINSERT_PROPERTY;
+static Atom clientSAVE_TARGETS;
+static Atom clientTARGET_SIZES;
+#ifdef SUPPORT_TEXT_TARGET
static Atom clientTEXT;
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
static Atom clientCOMPOUND_TEXT;
+#endif
static Atom clientUTF8_STRING;
-static Atom clientCLIPBOARD;
static char szAgentTARGETS[] = "TARGETS";
+#ifdef SUPPORT_TEXT_TARGET
static char szAgentTEXT[] = "TEXT";
-static char szAgentTIMESTAMP[] = "TIMESTAMP";
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
static char szAgentCOMPOUND_TEXT[] = "COMPOUND_TEXT";
+#endif
+static char szAgentTIMESTAMP[] = "TIMESTAMP";
+static char szAgentINCR[] = "INCR";
+static char szAgentMULTIPLE[] = "MULTIPLE";
+static char szAgentDELETE[] = "DELETE";
+static char szAgentINSERT_SELECTION[] = "INSERT_SELECTION";
+static char szAgentINSERT_PROPERTY[] = "INSERT_PROPERTY";
+static char szAgentSAVE_TARGETS[] = "SAVE_TARGETS";
+static char szAgentTARGET_SIZES[] = "TARGET_SIZES";
static char szAgentUTF8_STRING[] = "UTF8_STRING";
static char szAgentNX_CUT_BUFFER_CLIENT[] = "NX_CUT_BUFFER_CLIENT";
static char szAgentCLIPBOARD[] = "CLIPBOARD";
-/* number of milliseconds to wait for a conversion from the real X server. */
+/* Number of milliseconds to wait for a conversion from the real X server. */
#define CONVERSION_TIMEOUT 5000
/*
@@ -162,7 +250,7 @@ static char szAgentCLIPBOARD[] = "CLIPBOARD";
#define ACCUM_TIME 5000
/*
- * some helpers for debugging output
+ * Some helpers for debugging output
*/
static const char * getClientSelectionStageString(int stage)
@@ -179,9 +267,9 @@ static const char * getClientSelectionStageString(int stage)
}
#ifdef DEBUG
-#define printClientSelectionStage() do {fprintf(stderr, "%s: Current selection stage [%s]\n", __func__, getClientSelectionStageString(lastClientStage));} while (0)
+#define printClientSelectionStage(_index) do {fprintf(stderr, "%s: Current selection stage for selection [%d] is [%s]\n", __func__, _index, getClientSelectionStageString(lastClients[_index].stage));} while (0)
#else
-#define printClientSelectionStage()
+#define printClientSelectionStage(_index)
#endif
#define WINDOWID(ptr) (ptr) ? (ptr->drawable.id) : 0
@@ -189,10 +277,11 @@ static const char * getClientSelectionStageString(int stage)
#ifdef DEBUG
/*
- * see also nx-X11/lib/src/ErrDes.c
+ * See also nx-X11/lib/src/ErrDes.c
*
* We use our own version to avoid Xlib doing expensive calls.
* FIXME: Must check if XGetErrorText() is really causing traffic over the wire.
+ * FIXME: move this to a Utils.c or similar
*/
const char * getXErrorString(int code)
{
@@ -229,20 +318,20 @@ XFixesAgentInfoRec nxagentXFixesInfo = { -1, -1, -1, False };
extern Display *nxagentDisplay;
-static Bool validServerTargets(XlibAtom target);
-static void setClientSelectionStage(int stage);
-static void endTransfer(Bool success);
+static Bool isTextTarget(XlibAtom target);
+static void setClientSelectionStage(int index, int stage);
+static void endTransfer(int index, Bool success);
#define SELECTION_SUCCESS True
#define SELECTION_FAULT False
-static void transferSelection(int resource);
+static void transferSelectionFromXServer(int index, int resource);
#if 0
-static void resetSelectionOwner(void);
+static void resetSelectionOwnerOnXServer(void);
#endif
-static void initSelectionOwner(int index, Atom selection);
-static void clearSelectionOwner(int index);
-static void storeSelectionOwner(int index, Selection *sel);
+static void initSelectionOwnerData(int index);
+static void clearSelectionOwnerData(int index);
+static void storeSelectionOwnerData(int index, Selection *sel);
static Bool matchSelectionOwner(int index, ClientPtr pClient, WindowPtr pWindow);
-static void setSelectionOwner(Selection *pSelection);
+static void setSelectionOwnerOnXServer(Selection *pSelection);
static int sendEventToClient(ClientPtr client, xEvent *pEvents);
static void sendSelectionNotifyEventToClient(ClientPtr client,
Time time,
@@ -250,58 +339,90 @@ static void sendSelectionNotifyEventToClient(ClientPtr client,
Atom selection,
Atom target,
Atom property);
-static Status sendSelectionNotifyEventToServer(XSelectionEvent *event_to_send);
+static Status sendSelectionNotifyEventToXServer(XSelectionEvent *event_to_send);
+static void replyPendingRequestSelectionToXServer(int index, Bool success);
#ifdef DEBUG
static void printSelectionStat(int sel);
#endif
-static void replyRequestSelection(XEvent *X, Bool success);
+static void replyRequestSelectionToXServer(XEvent *X, Bool success);
+void handlePropertyTransferFromAgentToXserver(int index, XlibAtom property);
void nxagentPrintClipboardStat(char *);
+XlibAtom translateLocalToRemoteSelection(Atom local);
+XlibAtom translateLocalToRemoteTarget(Atom local);
+
#ifdef NXAGENT_TIMESTAMP
extern unsigned long startTime;
#endif
-static void printSelectionStat(int sel)
+static void printSelectionStat(int index)
{
- SelectionOwner lOwner = lastSelectionOwner[sel];
- Selection curSel = CurrentSelections[sel];
- char *s = NULL;
+ SelectionOwner lOwner = lastSelectionOwner[index];
+ Selection curSel = CurrentSelections[index];
+
+ fprintf(stderr, "selection [%d]:\n", index);
- fprintf(stderr, " owner is inside nxagent? %s\n", IS_INTERNAL_OWNER(sel) ? "yes" : "no");
+ fprintf(stderr, " selection Atom local [%d][%s] remote [%ld][%s]\n",
+ localSelelectionAtoms[index], NameForLocalAtom(localSelelectionAtoms[index]),
+ remoteSelectionAtoms[index], NameForRemoteAtom(remoteSelectionAtoms[index]));
+ fprintf(stderr, " owner side %s\n", IS_LOCAL_OWNER(index) ? "nxagent" : "real X server/none");
fprintf(stderr, " lastSelectionOwner[].client %s\n", nxagentClientInfoString(lOwner.client));
fprintf(stderr, " lastSelectionOwner[].window [0x%x]\n", lOwner.window);
if (lOwner.windowPtr)
- fprintf(stderr, " lastSelectionOwner[].windowPtr [%p] ([0x%x]\n", (void *)lOwner.windowPtr, WINDOWID(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);
- /*
- print the selection name. selection is _always_ a a remote Atom!
- */
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, lOwner.selection);
- fprintf(stderr, " lastSelectionOwner[].selection [% 4ld][%s] (%s)\n", lOwner.selection, validateString(s), lOwner.client ? "inside nxagent" : "remote X server");
- SAFE_XFree(s);
-#ifdef CLIENTIDS
- fprintf(stderr, " CurrentSelections[].client [%p] index [%d] PID [%d] Cmd [%s]\n",
- (void *)curSel.client,
- CLINDEX(curSel.client),
- GetClientPid(curSel.client),
- GetClientCmdName(curSel.client));
-#else
- fprintf(stderr, " CurrentSelections[].client [%p] index [%d]\n",
- (void *)curSel.client,
- CLINDEX(curSel.client));
-#endif
+ fprintf(stderr, " CurrentSelections[].client %s\n", nxagentClientInfoString(curSel.client));
fprintf(stderr, " CurrentSelections[].window [0x%x]\n", curSel.window);
return;
}
-void nxagentDumpClipboardStat(void)
+static void printLastClientStat(int index)
+{
+ lastClient lc = lastClients[index];
+ if (lc.windowPtr)
+ fprintf(stderr, " lastClients[].windowPtr (WindowPtr) [%p] ([0x%x])\n", (void *)lc.windowPtr, WINDOWID(lc.windowPtr));
+ else
+ fprintf(stderr, " lastClients[].windowPtr (WindowPtr) -\n");
+ fprintf(stderr, " lastClients[].clientPtr (ClientPtr) %s\n", nxagentClientInfoString(lc.clientPtr));
+ fprintf(stderr, " lastClients[].requestor (Window) [0x%x]\n", lc.requestor);
+ fprintf(stderr, " lastClients[].property (Atom) [% 4d][%s]\n", lc.property, NameForLocalAtom(lc.property));
+ fprintf(stderr, " lastClients[].target (Atom) [% 4d][%s]\n", lc.target, NameForLocalAtom(lc.target));
+ if (lc.time > 0)
+ fprintf(stderr, " lastClients[].time (Time) [%u] ([%u]ms ago)\n", lc.time, GetTimeInMillis() - lc.time);
+ else
+ fprintf(stderr, " lastClients[].time (Time) [%u]\n", lc.time);
+ if (lc.reqTime > 0)
+ fprintf(stderr, " lastClients[].reqTime (Time) [%u] ([%u]ms ago)\n", lc.reqTime, GetTimeInMillis() - lc.reqTime);
+ else
+ fprintf(stderr, " lastClients[].reqTime (Time) [%u]\n", lc.reqTime);
+ fprintf(stderr, " lastClients[].propertySize (ulong) [%lu]\n", lc.propertySize);
+ fprintf(stderr, " lastClients[].stage (ClientSelStage) [%d][%s]\n", lc.stage, getClientSelectionStageString(lc.stage));
+ fprintf(stderr, " lastClients[].resource (int) [%d]\n", lc.resource);
+}
+
+static void printLastServerStat(int index)
+{
+ lastServer ls = lastServers[index];
+ fprintf(stderr, " lastServer[].requestor (XlibWindow) [0x%lx]\n", ls.requestor);
+ fprintf(stderr, " lastServer[].property (XlibAtom) [% 4ld][%s]\n", ls.property, NameForRemoteAtom(ls.property));
+ fprintf(stderr, " lastServer[].target (XlibAtom) [% 4ld][%s]\n", ls.target, NameForRemoteAtom(ls.target));
+ fprintf(stderr, " lastServer[].time (Time) [%u]\n", ls.time);
+}
+
+static void printTargetCacheStat(int index)
{
- char *s = NULL;
+ fprintf(stderr, " targetCache[].type (int) [%d]\n", targetCache[index].type);
+ fprintf(stderr, " targetCache[].forLocal (Atom *) [%p]\n", (void *)targetCache[index].forLocal);
+ fprintf(stderr, " targetCache[].forRemote (XlibAtom *) [%p]\n", (void *)targetCache[index].forRemote);
+ fprintf(stderr, " targetCache[].numTargets (int) [%d]\n", targetCache[index].numTargets);
+}
+void nxagentDumpClipboardStat(void)
+{
fprintf(stderr, "/----- Clipboard internal status -----\n");
fprintf(stderr, " current time (Time) [%u]\n", GetTimeInMillis());
@@ -309,8 +430,7 @@ void nxagentDumpClipboardStat(void)
fprintf(stderr, " clientAccum (int) [%d]\n", clientAccum);
fprintf(stderr, " nxagentMaxSelections (int) [%d]\n", nxagentMaxSelections);
fprintf(stderr, " NumCurrentSelections (int) [%d]\n", NumCurrentSelections);
- fprintf(stderr, " serverWindow (Window) [0x%x]\n", serverWindow);
- fprintf(stderr, " nxagentLastClipboardClient (int) [%d]\n", nxagentLastClipboardClient);
+ fprintf(stderr, " serverWindow (XlibWindow) [0x%lx]\n", serverWindow);
fprintf(stderr, " Clipboard mode ");
switch(nxagentOption(Clipboard))
@@ -323,94 +443,106 @@ void nxagentDumpClipboardStat(void)
}
fprintf(stderr, "\n");
- fprintf(stderr, "lastServer\n");
- fprintf(stderr, " lastServerRequestor (Window) [0x%x]\n", lastServerRequestor);
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, lastServerProperty);
- fprintf(stderr, " lastServerProperty (Atom) [% 4ld][%s]\n", lastServerProperty, validateString(s));
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, lastServerTarget);
- fprintf(stderr, " lastServerTarget (Atom) [% 4ld][%s]\n", lastServerTarget, validateString(s));
- fprintf(stderr, " lastServerTime (Time) [%u]\n", lastServerTime);
-
- fprintf(stderr, "lastClient\n");
- if (lastClientWindowPtr)
- fprintf(stderr, " lastClientWindowPtr (WindowPtr) [%p] ([0x%x])\n", (void *)lastClientWindowPtr, WINDOWID(lastClientWindowPtr));
+ if (serverLastRequestedSelection == -1)
+ fprintf(stderr, " serverLastRequestedSelection [-1](uninitialized)\n");
else
- fprintf(stderr, " lastClientWindowPtr (WindowPtr) -\n");
- fprintf(stderr, " lastClientClientPtr (ClientPtr) %s\n", nxagentClientInfoString(lastClientClientPtr));
- fprintf(stderr, " lastClientRequestor (Window) [0x%x]\n", lastClientRequestor);
- fprintf(stderr, " lastClientProperty (Atom) [% 4d][%s]\n", lastClientProperty, NameForAtom(lastClientProperty));
- fprintf(stderr, " lastClientSelection (Atom) [% 4d][%s]\n", lastClientSelection, NameForAtom(lastClientSelection));
- fprintf(stderr, " lastClientTarget (Atom) [% 4d][%s]\n", lastClientTarget, NameForAtom(lastClientTarget));
- if (lastClientTime > 0)
- fprintf(stderr, " lastClientTime (Time) [%u] ([%u]ms ago)\n", lastClientTime, GetTimeInMillis() - lastClientTime);
- else
- fprintf(stderr, " lastClientTime (Time) [%u]\n", lastClientTime);
- if (lastClientReqTime > 0)
- fprintf(stderr, " lastClientReqTime (Time) [%u] ([%u]ms ago)\n", lastClientReqTime, GetTimeInMillis() - lastClientReqTime);
- else
- fprintf(stderr, " lastClientReqTime (Time) [%u]\n", lastClientReqTime);
- fprintf(stderr, " lastClientPropertySize (unsigned long) [%lu]\n", lastClientPropertySize);
- fprintf(stderr, " lastClientStage (ClientSelectionStage) [%d][%s]\n", lastClientStage, getClientSelectionStageString(lastClientStage));
-
- fprintf(stderr, "PRIMARY\n");
- printSelectionStat(nxagentPrimarySelection);
- fprintf(stderr, "CLIPBOARD\n");
- printSelectionStat(nxagentClipboardSelection);
-
- fprintf(stderr, "Atoms (remote X server)\n");
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverTARGETS);
- fprintf(stderr, " serverTARGETS [% 4ld][%s]\n", serverTARGETS, validateString(s));
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverTIMESTAMP);
- fprintf(stderr, " serverTIMESTAMP [% 4ld][%s]\n", serverTIMESTAMP, validateString(s));
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverTEXT);
- fprintf(stderr, " serverTEXT [% 4ld][%s]\n", serverTEXT, validateString(s));
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverCOMPOUND_TEXT);
- fprintf(stderr, " serverCOMPOUND_TEXT [% 4ld][%s]\n", serverCOMPOUND_TEXT, validateString(s));
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverUTF8_STRING);
- fprintf(stderr, " serverUTF8_STRING [% 4ld][%s]\n", serverUTF8_STRING, validateString(s));
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverTransToAgentProperty);
- fprintf(stderr, " serverTransToAgentProperty [% 4ld][%s]\n", serverTransFromAgentProperty, validateString(s));
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverTransFromAgentProperty);
- fprintf(stderr, " serverTransFromAgentProperty [% 4ld][%s]\n", serverTransToAgentProperty, validateString(s));
- SAFE_XFree(s); s = XGetAtomName(nxagentDisplay, serverLastRequestedSelection);
- fprintf(stderr, " serverLastRequestedSelection [% 4ld][%s]\n", serverLastRequestedSelection, validateString(s));
-
- fprintf(stderr, "Atoms (inside nxagent)\n");
- fprintf(stderr, " clientTARGETS [% 4d][%s]\n", clientTARGETS, NameForAtom(clientTARGETS));
- fprintf(stderr, " clientTIMESTAMP [% 4d][%s]\n", clientTIMESTAMP, NameForAtom(clientTIMESTAMP));
- fprintf(stderr, " clientTEXT [% 4d][%s]\n", clientTEXT, NameForAtom(clientTEXT));
- fprintf(stderr, " clientCOMPOUND_TEXT [% 4d][%s]\n", clientCOMPOUND_TEXT, NameForAtom(clientCOMPOUND_TEXT));
- fprintf(stderr, " clientUTF8_STRING [% 4d][%s]\n", clientUTF8_STRING, NameForAtom(clientUTF8_STRING));
- fprintf(stderr, " clientCLIPBOARD [% 4d][%s]\n", clientCLIPBOARD, NameForAtom(clientCLIPBOARD));
- fprintf(stderr, " clientCutProperty [% 4d][%s]\n", clientCutProperty, NameForAtom(clientCutProperty));
+ fprintf(stderr, " serverLastRequestedSelection [% 4ld][%s]\n", serverLastRequestedSelection, NameForRemoteAtom(serverLastRequestedSelection));
- fprintf(stderr, "\\------------------------------------------------------------------------------\n");
+ fprintf(stderr, "Compile time settings\n");
+#ifdef PRINT_CLIPBOARD_CONTENT_ON_DEBUG
+ fprintf(stderr, " PRINT_CLIPBOARD_CONTENT_ON_DEBUG [enabled]\n");
+#else
+ fprintf(stderr, " PRINT_CLIPBOARD_CONTENT_ON_DEBUG [disabled]\n");
+#endif
+#ifdef SUPPORT_TEXT_TARGET
+ fprintf(stderr, " SUPPORT_TEXT_TARGET [enabled]\n");
+#else
+ fprintf(stderr, " SUPPORT_TEXT_TARGET [disabled]\n");
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
+ fprintf(stderr, " SUPPORT_COMPOUND_TEXT_TARGET [enabled]\n");
+#else
+ fprintf(stderr, " SUPPORT_COMPOUND_TEXT_TARGET [disabled]\n");
+#endif
+
+#define WIDTH 32
+ Atom cl = 0;
+ XlibAtom sv = 0;
+ int len = WIDTH;
+
+ fprintf(stderr, "Atoms local%*sremote\n", WIDTH - 5, "");
+ cl = clientTARGETS; sv = serverTARGETS; len = (int)(WIDTH - 9 - strlen(NameForLocalAtom(cl)));
+ fprintf(stderr, " TARGETS [% 4d][%s]%*s [% 4ld][%s]\n", cl, NameForLocalAtom(cl), len, "", sv, NameForRemoteAtom(sv));
- SAFE_XFree(s);
+ cl = clientTIMESTAMP; sv = serverTIMESTAMP; len = (int)(WIDTH - 9 - strlen(NameForLocalAtom(cl)));
+ fprintf(stderr, " TIMESTAMP [% 4d][%s]%*s [% 4ld][%s]\n", cl, NameForLocalAtom(cl), len, "", sv, NameForRemoteAtom(sv));
+
+#ifdef SUPPORT_TEXT_TARGET
+ cl = clientTEXT; sv = serverTEXT; len = (int)(WIDTH - 9 - strlen(NameForLocalAtom(cl)));
+ fprintf(stderr, " TEXT [% 4d][%s]%*s [% 4ld][%s]\n", cl, NameForLocalAtom(cl), len, "", sv, NameForRemoteAtom(sv));
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
+ cl = clientCOMPOUND_TEXT; sv = serverCOMPOUND_TEXT; len = (int)(WIDTH - 9 - strlen(NameForLocalAtom(cl)));
+ fprintf(stderr, " COMPOUND_TEXT [% 4d][%s]%*s [% 4ld][%s]\n", cl, NameForLocalAtom(cl), len, "", sv, NameForRemoteAtom(sv));
+#endif
+
+ cl = clientUTF8_STRING; sv = serverUTF8_STRING; len = (int)(WIDTH - 9 - strlen(NameForLocalAtom(cl)));
+ fprintf(stderr, " UTF8_STRING [% 4d][%s]%*s [% 4ld][%s]\n", cl, NameForLocalAtom(cl), len, "", sv, NameForRemoteAtom(sv));
+
+ sv = serverTransToAgentProperty;
+ fprintf(stderr, " serverTransToAgentProperty - %*s[% 4ld][%s]\n", WIDTH - 2, "", sv, NameForRemoteAtom(sv));
+
+ sv = serverTransFromAgentProperty;
+ fprintf(stderr, " serverTransFromAgentProperty - %*s[% 4ld][%s]\n", WIDTH - 2, "", sv, NameForRemoteAtom(sv));
+
+ cl = clientCutProperty; len = (int)(WIDTH - 9 - strlen(NameForLocalAtom(cl)));
+ fprintf(stderr, " clientCutProperty [% 4d][%s]%*s\n", cl, NameForLocalAtom(cl), len + 2, "-" );
+
+ for (int index = 0; index < nxagentMaxSelections; index++)
+ {
+ printSelectionStat(index);
+ printLastClientStat(index);
+ printLastServerStat(index);
+ printTargetCacheStat(index);
+ }
+
+ fprintf(stderr, "\\------------------------------------------------------------------------------\n");
}
/*
* Helper to handle data transfer
*/
-static void setClientSelectionStage(int stage)
+static void resetClientSelectionStage(int index)
{
#ifdef DEBUG
- fprintf(stderr, "%s: Changing selection stage from [%s] to [%s]\n", __func__,
- getClientSelectionStageString(lastClientStage), getClientSelectionStageString(stage));
+ fprintf(stderr, "%s: Resetting selection stage for [%d]\n", __func__, index);
#endif
- lastClientStage = stage;
+ lastClients[index].stage = SelectionStageNone;
+ lastClients[index].windowPtr = NULL;
+ lastClients[index].clientPtr = NULL;
+ lastClients[index].requestor = 0;
+ lastClients[index].property = 0;
+ lastClients[index].target = 0;
+ lastClients[index].time = 0;
+ lastClients[index].reqTime = 0;
+ lastClients[index].propertySize = 0;
+ lastClients[index].resource = -1;
+}
+
+static void setClientSelectionStage(int index, int stage)
+{
if (stage == SelectionStageNone)
{
- lastClientWindowPtr = NULL;
- lastClientClientPtr = NULL;
- lastClientRequestor = 0;
- lastClientProperty = 0;
- lastClientSelection = 0;
- lastClientTarget = 0;
- lastClientTime = 0;
- lastClientReqTime = 0;
- lastClientPropertySize = 0;
+ resetClientSelectionStage(index);
+ }
+ else
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Changing selection stage for [%d] from [%s] to [%s]\n", __func__, index,
+ getClientSelectionStageString(lastClients[index].stage), getClientSelectionStageString(stage));
+ #endif
+ lastClients[index].stage = stage;
}
}
@@ -425,11 +557,11 @@ int GetWindowProperty(WindowPtr pWin, Atom property, long longOffset, long longL
/*
* Send a SelectionNotify event to the real X server and do some error
- * handling (in DEBUG mode)
+ * handling (in DEBUG mode).
*/
-static Status sendSelectionNotifyEventToServer(XSelectionEvent *event_to_send)
+static Status sendSelectionNotifyEventToXServer(XSelectionEvent *event_to_send)
{
- Window w = event_to_send->requestor;
+ XlibWindow w = event_to_send->requestor;
event_to_send->type = SelectionNotify;
event_to_send->send_event = True;
@@ -445,17 +577,17 @@ static Status sendSelectionNotifyEventToServer(XSelectionEvent *event_to_send)
*/
if (result == 0)
{
- fprintf(stderr, "%s: XSendEvent to [0x%x] failed.\n", __func__, w);
+ fprintf(stderr, "%s: XSendEvent to [0x%lx] 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));
+ fprintf(stderr, "%s: WARNING! XSendEvent to [0x%lx] failed: %s\n", __func__, w, getXErrorString(result));
}
else
{
- fprintf(stderr, "%s: XSendEvent() successfully sent to [0x%x]\n", __func__, w);
+ fprintf(stderr, "%s: XSendEvent() successfully sent to [0x%lx]\n", __func__, w);
}
}
#endif
@@ -471,11 +603,11 @@ static int sendEventToClient(ClientPtr client, xEvent *pEvents)
}
static void sendSelectionNotifyEventToClient(ClientPtr client,
- Time time,
- Window requestor,
- Atom selection,
- Atom target,
- Atom property)
+ Time time,
+ Window requestor,
+ Atom selection,
+ Atom target,
+ Atom property)
{
/*
* Check if the client is still valid.
@@ -499,22 +631,21 @@ static void sendSelectionNotifyEventToClient(ClientPtr client,
#ifdef DEBUG
if (property == None)
- fprintf(stderr, "%s: Denying request to client %s.\n", __func__,
- nxagentClientInfoString(client));
+ fprintf(stderr, "%s: Denying request to client %s - event time [%u].\n", __func__,
+ nxagentClientInfoString(client), time);
else
- fprintf(stderr, "%s: Sending event to client %s.\n", __func__,
- nxagentClientInfoString(client));
+ fprintf(stderr, "%s: Sending event to client %s - event time [%u].\n", __func__,
+ nxagentClientInfoString(client), time);
#endif
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.
+ * Check if target is a valid text content type target sent by the real X
+ * server, like .e.g XA_STRING or UTF8_STRING.
*/
-static Bool validServerTargets(XlibAtom target)
+static Bool isTextTarget(XlibAtom target)
{
if (target == XA_STRING)
{
@@ -523,6 +654,7 @@ static Bool validServerTargets(XlibAtom target)
#endif
return True;
}
+#ifdef SUPPORT_TEXT_TARGET
else if (target == serverTEXT)
{
#ifdef DEBUG
@@ -530,14 +662,8 @@ static Bool validServerTargets(XlibAtom target)
#endif
return True;
}
- /* by dimbor */
- else if (target == serverUTF8_STRING)
- {
- #ifdef DEBUG
- fprintf(stderr, "%s: valid target [UTF8_STRING].\n", __func__);
- #endif
- return True;
- }
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
else if (target == serverCOMPOUND_TEXT)
{
#ifdef DEBUG
@@ -545,30 +671,24 @@ static Bool validServerTargets(XlibAtom target)
#endif
return True;
}
- else if (target == serverTARGETS)
- {
- #ifdef DEBUG
- fprintf(stderr, "%s: special target [TARGETS].\n", __func__);
- #endif
- return False;
- }
- else if (target == serverTIMESTAMP)
+#endif
+ else if (target == serverUTF8_STRING)
{
#ifdef DEBUG
- fprintf(stderr, "%s: special target [TIMESTAMP].\n", __func__);
+ fprintf(stderr, "%s: valid target [UTF8_STRING].\n", __func__);
#endif
- return False;
+ return True;
}
+ /* FIXME: add text/plain */
#ifdef DEBUG
- fprintf(stderr, "%s: invalid target [%lu].\n", __func__, target);
+ fprintf(stderr, "%s: not a text target [%lu].\n", __func__, target);
#endif
return False;
}
-static void initSelectionOwner(int index, Atom selection)
+static void initSelectionOwnerData(int index)
{
- lastSelectionOwner[index].selection = selection;
lastSelectionOwner[index].client = NullClient;
lastSelectionOwner[index].window = screenInfo.screens[0]->root->drawable.id;
lastSelectionOwner[index].windowPtr = NULL;
@@ -576,15 +696,15 @@ static void initSelectionOwner(int index, Atom selection)
}
/* there's no owner on nxagent side anymore */
-static void clearSelectionOwner(int index)
+static void clearSelectionOwnerData(int index)
{
- lastSelectionOwner[index].client = NULL;
+ lastSelectionOwner[index].client = NullClient;
lastSelectionOwner[index].window = None;
lastSelectionOwner[index].windowPtr = NULL;
lastSelectionOwner[index].lastTimeChanged = GetTimeInMillis();
}
-static void storeSelectionOwner(int index, Selection *sel)
+static void storeSelectionOwnerData(int index, Selection *sel)
{
lastSelectionOwner[index].client = sel->client;
lastSelectionOwner[index].window = sel->window;
@@ -603,6 +723,7 @@ static Bool matchSelectionOwner(int index, ClientPtr pClient, WindowPtr pWindow)
* Attention: does not work properly when both client AND window
* are passed as setClientSelectionStage(None) will also clear
* the lastClientWindowPtr!
+ * This is only called from Client.c and Window.c
*/
void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow)
{
@@ -611,74 +732,164 @@ void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow)
(void *) pClient, CLINDEX(pClient), (void *) pWindow, WINDOWID(pWindow));
#endif
- /*
- * Only for PRIMARY and CLIPBOARD selections.
- */
-
- for (int i = 0; i < nxagentMaxSelections; i++)
+ /* FIXME: there's almost identical code in nxagentClipboardInit */
+ for (int index = 0; index < nxagentMaxSelections; index++)
{
- if (matchSelectionOwner(i, pClient, pWindow))
+ if (matchSelectionOwner(index, pClient, pWindow))
{
#ifdef TEST
- fprintf(stderr, "%s: Resetting state with client [%p] window [%p].\n", __func__,
- (void *) pClient, (void *) pWindow);
+ fprintf(stderr, "%s: Resetting state [%d] with client [%p] window [%p].\n", __func__,
+ index, (void *) pClient, (void *) pWindow);
#endif
- clearSelectionOwner(i);
+ clearSelectionOwnerData(index);
- setClientSelectionStage(SelectionStageNone);
+ setClientSelectionStage(index, SelectionStageNone);
- lastServerRequestor = None;
+ replyPendingRequestSelectionToXServer(index, False);
}
- }
- if (pWindow && pWindow == lastClientWindowPtr)
- {
- setClientSelectionStage(SelectionStageNone);
+ if (pWindow && pWindow == lastClients[index].windowPtr)
+ {
+ setClientSelectionStage(index, SelectionStageNone);
+ }
}
}
/*
* Find the index of the lastSelectionOwner with the selection
- * sel. sel is an atom on the real X server.
+ * sel. sel is an atom on the real X server. If the index cannot be
+ * determined it will return -1.
*/
-int nxagentFindLastSelectionOwnerIndex(XlibAtom sel)
+int nxagentFindRemoteSelectionIndex(XlibAtom sel)
{
- int i = 0;
- while (i < nxagentMaxSelections &&
- lastSelectionOwner[i].selection != sel)
+ for (int index = 0; index < nxagentMaxSelections; index++)
{
- i++;
+ if (remoteSelectionAtoms[index] == sel)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: remote selection [%ld][%s] belongs to index [%d]\n", __func__, sel, NameForRemoteAtom(sel), index);
+ #endif
+ return index;
+ }
}
- return i;
+ #ifdef DEBUG
+ fprintf(stderr, "%s: remote selection [%ld][%s] does not belong to any index!\n", __func__, sel, NameForRemoteAtom(sel));
+ #endif
+ return -1;
}
/*
* Find the index of CurrentSelection with the selection
- * sel. sel is an internal atom.
+ * sel. sel is a local atom. If the index cannot be
+ * determined it will return -1.
*/
int nxagentFindCurrentSelectionIndex(Atom sel)
{
- int i = 0;
- while (i < NumCurrentSelections &&
- CurrentSelections[i].selection != sel)
+ /*
+ * Normally you'd expect the loop going up to
+ * NumCurrentSelections. But the dix code will increase that number
+ * (but not nxagentMaxSelections) when drag and drop comes into
+ * play. In that case this helper will report a match for other
+ * selections than the ones the clipboard code knows about. The
+ * subsequent code will then use a higher index which will be used
+ * by the clipboard code and will lead to out of range data reads
+ * (and writes!). Therefore we take nxagentMaxSelections here. The
+ * startup code ensures that both arrays will refer to the same
+ * selection for the first nxagentMaxSelections selection atoms.
+ */
+
+ /* for (int index = 0; index < NumCurrentSelections; index++) */
+ for (int index = 0; index < nxagentMaxSelections; index++)
{
- i++;
+ if (CurrentSelections[index].selection == sel)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: selection [%d][%s] belongs to index [%d]\n", __func__, sel, NameForLocalAtom(sel), index);
+ #endif
+ return index;
+ }
+ }
+ #ifdef DEBUG
+ fprintf(stderr, "%s: selection [%d][%s] does not belong to any index!\n", __func__, sel, NameForLocalAtom(sel));
+ #endif
+ return -1;
+}
+
+void cacheTargetsForLocal(int index, Atom* targets, int numTargets)
+{
+ #ifdef DEBUG
+ fprintf(stderr, "%s: caching [%d] targets for local requests\n", __func__, numTargets);
+ #endif
+
+ SAFE_free(targetCache[index].forLocal);
+ SAFE_free(targetCache[index].forRemote);
+ targetCache[index].type = FOR_LOCAL;
+ targetCache[index].forLocal = targets;
+ targetCache[index].numTargets = numTargets;
+}
+
+void cacheTargetsForRemote(int index, XlibAtom* targets, int numTargets)
+{
+ #ifdef DEBUG
+ fprintf(stderr, "%s: caching [%d] targets for remote requests\n", __func__, numTargets);
+ #endif
+
+ SAFE_free(targetCache[index].forLocal);
+ SAFE_free(targetCache[index].forRemote);
+ targetCache[index].type = FOR_REMOTE;
+ targetCache[index].forRemote = targets;
+ targetCache[index].numTargets = numTargets;
+}
+
+/* This is called on init, reconnect and SelectionClear. */
+void invalidateTargetCache(int index)
+{
+ #ifdef DEBUG
+ fprintf(stderr, "%s: invalidating target cache [%d]\n", __func__, index);
+ #endif
+
+ SAFE_free(targetCache[index].forLocal);
+ SAFE_free(targetCache[index].forRemote);
+ targetCache[index].type = EMPTY;
+ targetCache[index].numTargets = 0;
+}
+
+void invalidateTargetCaches(void)
+{
+ #ifdef DEBUG
+ fprintf(stderr, "%s: invalidating all target caches\n", __func__);
+ #endif
+
+ for (int index = 0; index < nxagentMaxSelections; index++)
+ {
+ SAFE_free(targetCache[index].forLocal);
+ SAFE_free(targetCache[index].forRemote);
+ targetCache[index].type = EMPTY;
+ targetCache[index].numTargets = 0;
}
- 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.
+ * X server claims the selection ownership we have/had.
+ * Three versions of this routine with different parameter types.
*/
-void nxagentHandleSelectionClearFromXServer(XEvent *X)
+void nxagentHandleSelectionClearFromXServerByIndex(int index)
{
#ifdef DEBUG
- fprintf(stderr, "%s: SelectionClear event for selection [%lu].\n", __func__, X->xselectionclear.selection);
+ fprintf(stderr, "%s: SelectionClear event for selection index [%u].\n", __func__, index);
#endif
+ if (index == -1)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: ignoring index -1 - doing nothing.\n", __func__);
+ #endif
+ return;
+ }
+
if (!agentClipboardInitialized)
{
#ifdef DEBUG
@@ -695,33 +906,56 @@ void nxagentHandleSelectionClearFromXServer(XEvent *X)
return;
}
- int i = nxagentFindLastSelectionOwnerIndex(X->xselectionclear.selection);
- if (i < nxagentMaxSelections)
+ if (IS_LOCAL_OWNER(index))
{
- if (IS_INTERNAL_OWNER(i))
- {
- /* 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;
-
- sendEventToClient(lastSelectionOwner[i].client, &x);
- }
+ /* 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[index].window;
+ x.u.selectionClear.atom = CurrentSelections[index].selection;
+
+ sendEventToClient(lastSelectionOwner[index].client, &x);
/*
- * set the root window with the NullClient as selection owner. Our
+ * Set the root window with the NullClient as selection owner. Our
* clients asking for the owner via XGetSelectionOwner() will get
- * these for an answer
+ * this for an answer.
*/
- CurrentSelections[i].window = screenInfo.screens[0]->root->drawable.id;
- CurrentSelections[i].client = NullClient;
+ CurrentSelections[index].window = screenInfo.screens[0]->root->drawable.id;
+ CurrentSelections[index].client = NullClient;
+
+ clearSelectionOwnerData(index);
+
+ setClientSelectionStage(index, SelectionStageNone);
- clearSelectionOwner(i);
+ invalidateTargetCache(index);
+ }
+ else
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: selection already cleared - doing nothing.\n", __func__);
+ #endif
}
+}
+
+void nxagentHandleSelectionClearFromXServerByAtom(XlibAtom sel)
+{
+ #ifdef DEBUG
+ fprintf(stderr, "---------\n%s: SelectionClear event for remote selection atom [%lu][%s].\n", __func__, sel, NameForRemoteAtom(sel));
+ #endif
- setClientSelectionStage(SelectionStageNone);
+ nxagentHandleSelectionClearFromXServerByIndex(nxagentFindRemoteSelectionIndex(sel));
+}
+
+void nxagentHandleSelectionClearFromXServer(XEvent *X)
+{
+ #ifdef DEBUG
+ fprintf(stderr, "---------\n%s: SelectionClear event for selection [%lu][%s] window [0x%lx] time [%lu].\n",
+ __func__, X->xselectionclear.selection, NameForRemoteAtom(X->xselectionclear.selection),
+ X->xselectionclear.window, X->xselectionclear.time);
+ #endif
+ nxagentHandleSelectionClearFromXServerByAtom(X->xselectionclear.selection);
}
/*
@@ -729,7 +963,7 @@ void nxagentHandleSelectionClearFromXServer(XEvent *X)
* event X. If success is True take the property from the event, else
* take None (which reports "failed/denied" to the requestor).
*/
-static void replyRequestSelection(XEvent *X, Bool success)
+static void replyRequestSelectionToXServer(XEvent *X, Bool success)
{
XSelectionEvent eventSelection = {
.requestor = X->xselectionrequest.requestor,
@@ -747,7 +981,7 @@ static void replyRequestSelection(XEvent *X, Bool success)
eventSelection.property = None;
}
- sendSelectionNotifyEventToServer(&eventSelection);
+ sendSelectionNotifyEventToXServer(&eventSelection);
}
/*
@@ -759,24 +993,20 @@ static void replyRequestSelection(XEvent *X, Bool success)
*/
void nxagentHandleSelectionRequestFromXServer(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);
+ XlibAtom target = X->xselectionrequest.target;
- 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);
+ #ifdef DEBUG
+ fprintf(stderr, "---------\n%s: Received SelectionRequestEvent from real server: selection [%ld][%s] " \
+ "target [%ld][%s] requestor [display[%s]/0x%lx] destination [%ld][%s] time [%lu]\n",
+ __func__,
+ X->xselectionrequest.selection, NameForRemoteAtom(X->xselectionrequest.selection),
+ target, NameForRemoteAtom(target),
+ DisplayString(nxagentDisplay), X->xselectionrequest.requestor,
+ X->xselectionrequest.property, NameForRemoteAtom(X->xselectionrequest.property),
+ X->xselectionrequest.time);
+ if (X->xselectionrequest.requestor == serverWindow)
+ {
+ fprintf(stderr, "%s: this event has been sent by nxagent!\n", __func__);;
}
#endif
@@ -788,68 +1018,84 @@ void nxagentHandleSelectionRequestFromXServer(XEvent *X)
return;
}
- /* lastServerRequestor in non-NULL (= we are currently in the transfer phase) */
- if (lastServerRequestor != None)
+ /* The selection in this request is none we own. */
+ int index = nxagentFindRemoteSelectionIndex(X->xselectionrequest.selection);
+ if (index == -1)
{
#ifdef DEBUG
- fprintf(stderr, "%s: denying additional request during transfer phase.\n", __func__);
+ fprintf(stderr, "%s: not owning selection [%ld] - denying request.\n", __func__, X->xselectionrequest.selection);
#endif
- replyRequestSelection(X, False);
+ replyRequestSelectionToXServer(X, False);
return;
}
- /* the selection in this request is none we own. */
+ if (!IS_LOCAL_OWNER(index))
{
- int i = nxagentFindLastSelectionOwnerIndex(X->xselectionrequest.selection);
- if (i == nxagentMaxSelections)
- {
- #ifdef DEBUG
- fprintf(stderr, "%s: not owning selection [%ld] - denying request.\n", __func__, X->xselectionrequest.selection);
- #endif
+ #ifdef DEBUG
+ fprintf(stderr, "%s: no local owner for selection [%ld] - denying request.\n", __func__, X->xselectionrequest.selection);
+ #endif
+ replyRequestSelectionToXServer(X, False);
+ return;
+ }
- replyRequestSelection(X, False);
- return;
- }
+ #ifdef DEBUG
+ fprintf(stderr, "%s: lastServers[%d].requestor [0x%lx].\n", __func__, index, lastServers[index].requestor);
+ #endif
+
+ /* lastServers[index].requestor is non-NULL (= we are currently in the transfer phase) */
+ if (lastServers[index].requestor != None)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: denying additional request during transfer phase.\n", __func__);
+ #endif
+
+ replyRequestSelectionToXServer(X, False);
+ return;
}
- /* this is a special request like TARGETS or TIMESTAMP */
- if (!validServerTargets(X->xselectionrequest.target))
+ if (target == serverTARGETS)
{
- if (X->xselectionrequest.target == serverTARGETS)
+ /*
+ * In TextClipboard mode answer with a predefined list of
+ * targets. This is just the previous implementation of handling
+ * the clipboard.
+ */
+ if (nxagentOption(TextClipboard))
{
/*
- * the selection request target is TARGETS. The requestor is
+ * The selection request target is TARGETS. The requestor is
* asking for a list of supported data formats.
*
* The selection does not matter here, we will return this for
* PRIMARY and CLIPBOARD.
*
- * The list is aligned with the one in nxagentConvertSelection.
- *
- * FIXME: the perfect solution should not just answer with
- * XA_STRING but ask the real owner what format it supports. The
- * result should then be sent to the original requestor.
+ * The list is aligned with the one in nxagentConvertSelection()
+ * and in isTextTarget().
*/
- long targets[] = {XA_STRING, serverUTF8_STRING, serverTEXT, serverCOMPOUND_TEXT, serverTARGETS, serverTIMESTAMP};
+ XlibAtom targets[] = {XA_STRING,
+ serverUTF8_STRING,
+#ifdef SUPPORT_TEXT_TARGET
+ serverTEXT,
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
+ serverCOMPOUND_TEXT,
+#endif
+ serverTARGETS,
+ serverTIMESTAMP};
int numTargets = sizeof(targets) / sizeof(targets[0]);
#ifdef DEBUG
+ fprintf(stderr, "%s: Sending %d available targets:\n", __func__, numTargets);
+ for (int i = 0; i < numTargets; i++)
{
- fprintf(stderr, "%s: Sending %d available targets:\n", __func__, numTargets);
- for (int i = 0; i < numTargets; i++)
- {
- char *s = XGetAtomName(nxagentDisplay, targets[i]);
- fprintf(stderr, "%s: %ld %s\n", __func__, targets[i], s);
- SAFE_XFree(s);
- }
- fprintf(stderr, "\n");
+ fprintf(stderr, "%s: %ld %s\n", __func__, targets[i], NameForRemoteAtom(targets[i]));
}
#endif
/*
- * pass on the requested list by setting the property provided
+ * Pass on the requested list by setting the property provided
* by the requestor accordingly.
*/
XChangeProperty(nxagentDisplay,
@@ -858,165 +1104,320 @@ void nxagentHandleSelectionRequestFromXServer(XEvent *X)
XInternAtom(nxagentDisplay, "ATOM", 0),
32,
PropModeReplace,
- (unsigned char*)&targets,
+ (unsigned char*)targets,
numTargets);
- replyRequestSelection(X, True);
+ replyRequestSelectionToXServer(X, True);
+ return;
}
- else if (X->xselectionrequest.target == serverTIMESTAMP)
+ else
{
/*
- * 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
- * FIXME: for a 32 bit property list we need to pass a "long" array, not "char"!
+ * Shortcut: Some applications tend to post multiple
+ * SelectionRequests. Further it can happen that multiple
+ * clients are interested in clipboard content. If we already
+ * know the answer and no intermediate SelectionOwner event
+ * occured we can answer with the cached list of targets.
*/
- int i = nxagentFindLastSelectionOwnerIndex(X->xselectionrequest.selection);
- if (i < nxagentMaxSelections)
+ if (targetCache[index].type == FOR_REMOTE && targetCache[index].forRemote)
{
+ XlibAtom *targets = targetCache[index].forRemote;
+ unsigned int numTargets = targetCache[index].numTargets;
+
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Sending %d cached targets to remote requestor:\n", __func__, numTargets);
+ for (int i = 0; i < numTargets; i++)
+ {
+ fprintf(stderr, "%s: %ld %s\n", __func__, targets[i], NameForRemoteAtom(targets[i]));
+ }
+ #endif
+
XChangeProperty(nxagentDisplay,
X->xselectionrequest.requestor,
X->xselectionrequest.property,
- XA_INTEGER,
+ XInternAtom(nxagentDisplay, "ATOM", 0),
32,
PropModeReplace,
- (unsigned char *) &lastSelectionOwner[i].lastTimeChanged,
- 1);
- replyRequestSelection(X, True);
+ (unsigned char *)targets,
+ numTargets);
+
+ replyRequestSelectionToXServer(X, True);
+ return;
}
}
- else
+ }
+ else if (target == serverTIMESTAMP)
+ {
+ /*
+ * 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
+ * FIXME: for a 32 bit property list we need to pass a "long" array, not "char"!
+ */
+
+ XChangeProperty(nxagentDisplay,
+ X->xselectionrequest.requestor,
+ X->xselectionrequest.property,
+ XA_INTEGER,
+ 32,
+ PropModeReplace,
+ (unsigned char *) &lastSelectionOwner[index].lastTimeChanged,
+ 1);
+ replyRequestSelectionToXServer(X, True);
+ return;
+ }
+ else if (target == serverMULTIPLE)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (currently) unsupported target [MULTIPLE] - denying request.\n", __func__);
+ #endif
+ replyRequestSelectionToXServer(X, False);
+ return;
+ }
+ else if (target == serverDELETE)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (currently) unsupported target [DELETE] - denying request.\n", __func__);
+ #endif
+ replyRequestSelectionToXServer(X, False);
+ return;
+ }
+ else if (target == serverINSERT_SELECTION)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (currently) unsupported target [INSERT_SELECTION] - denying request.\n", __func__);
+ #endif
+ replyRequestSelectionToXServer(X, False);
+ return;
+ }
+ else if (target == serverINSERT_PROPERTY)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (currently) unsupported target [INSERT_PROPERTY] - denying request.\n", __func__);
+ #endif
+ replyRequestSelectionToXServer(X, False);
+ return;
+ }
+ else if (target == serverSAVE_TARGETS)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (currently) unsupported target [SAVE_TARGETS] - denying request.\n", __func__);
+ #endif
+ replyRequestSelectionToXServer(X, False);
+ return;
+ }
+ else if (target == serverTARGET_SIZES)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (currently) unsupported target [TARGET_SIZES] - denying request.\n", __func__);
+ #endif
+ replyRequestSelectionToXServer(X, False);
+ return;
+ }
+
+ if (nxagentOption(TextClipboard))
+ {
+ if (!isTextTarget(target))
{
- /*
- * unknown special request - probably bug! Check if this code handles all cases
- * that are handled in validServerTargets!
- */
#ifdef DEBUG
- fprintf(stderr, "%s: unknown special target [%ld] - denying request.\n", __func__, X->xselectionrequest.target);
+ fprintf(stderr, "%s: denying request for non-text target [%ld][%s].\n", __func__,
+ target, NameForRemoteAtom(target));
#endif
- replyRequestSelection(X, False);
+
+ replyRequestSelectionToXServer(X, False);
+
+ return;
}
- return;
+ /* go on, target is acceptable */
}
- /*
- * reaching this means the request is a normal, valid request. We
- * can process it now.
- */
+ #ifdef DEBUG
+ fprintf(stderr, "%s: target [%ld][%s].\n", __func__, target,
+ NameForRemoteAtom(target));
+ #endif
/*
- * This is required for nxagentGetClipboardWindow.
+ * Reaching this line means the request is a normal, valid
+ * request. We can process it now.
*/
- serverLastRequestedSelection = X->xselectionrequest.selection;
- /* find the index of the requested selection */
- int i = nxagentFindLastSelectionOwnerIndex(X->xselectionrequest.selection);
- if (i < nxagentMaxSelections)
+ if (!nxagentOption(TextClipboard))
{
- if (lastClientWindowPtr != NULL && IS_INTERNAL_OWNER(i))
+ /* Optimization: if we have a current target cache check if the
+ * requested target is supported by the owner. If not we can take
+ * a shortcut and deny the request immediately without doing any
+ * further communication. */
+ if (targetCache[index].type == FOR_REMOTE && targetCache[index].forRemote)
{
- /*
- * Request the real X server to transfer the selection content
- * to the NX_CUT_BUFFER_SERVER property of the serverWindow.
- * FIXME: document how we can end up here
- */
- XConvertSelection(nxagentDisplay, CurrentSelections[i].selection,
- X->xselectionrequest.target, serverTransToAgentProperty,
- serverWindow, lastClientTime);
+ XlibAtom *targets = targetCache[index].forRemote;
#ifdef DEBUG
- fprintf(stderr, "%s: Sent XConvertSelection.\n", __func__);
+ fprintf(stderr, "%s: Checking target validity\n", __func__);
#endif
+ Bool match = False;
+ for (int i = 0; i < targetCache[index].numTargets; i++)
+ {
+ if (targets[i] == target)
+ {
+ match = True;
+ break;
+ }
+ }
+ if (!match)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: target [%ld][%s] is not supported by the owner - denying request.\n",
+ __func__, X->xselectionrequest.target, NameForRemoteAtom(X->xselectionrequest.target));
+ #endif
+ replyRequestSelectionToXServer(X, False);
+ return;
+ }
}
else
{
/*
- * if one of our clients owns the selection we ask it to copy
- * the selection to the clientCutProperty on nxagent's root
- * window in the first step. We then later push that property's
- * content to the real X server.
+ * At this stage we know a remote client has requested a
+ * selection target without having retrieved the list of
+ * supported targets first.
*/
- if (IS_INTERNAL_OWNER(i) &&
- (nxagentOption(Clipboard) == ClipboardServer ||
- nxagentOption(Clipboard) == ClipboardBoth))
+ #ifdef DEBUG
+ if (target != serverTARGETS)
{
- /*
- * 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;
-
- /* 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.*/
+ fprintf(stderr, "%s: WARNING: remote client has not retrieved TARGETS before asking for selection!\n",
+ __func__);
+ }
+ #endif
+ }
+ }
- /*
- * 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;
- */
+ /*
+ * This is required for nxagentGetClipboardWindow.
+ */
+ serverLastRequestedSelection = X->xselectionrequest.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;
+ if (!(nxagentOption(Clipboard) == ClipboardServer ||
+ nxagentOption(Clipboard) == ClipboardBoth))
+ {
+ #ifdef DEBUG
+ fprintf (stderr, "%s: clipboard (partly) disabled - denying request.\n", __func__);
+ #endif
- sendEventToClient(lastSelectionOwner[i].client, &x);
+ /* deny the request */
+ replyRequestSelectionToXServer(X, False);
+ return;
+ }
- #ifdef DEBUG
- fprintf(stderr, "%s: sent SelectionRequest event to client %s property [%d][%s]" \
- "target [%d][%s] requestor [0x%x].\n", __func__,
- nxagentClientInfoString(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
+ /*
+ * If one of our clients owns the selection we ask it to copy
+ * the selection to the clientCutProperty on nxagent's root
+ * window in the first step. We then later push that property's
+ * content to the real X server.
+ */
+ if (IS_LOCAL_OWNER(index))
+ {
+ /*
+ * Store who on the real X server requested the data and how
+ * and where it wants to have it.
+ */
+ lastServers[index].property = X->xselectionrequest.property;
+ lastServers[index].requestor = X->xselectionrequest.requestor;
+ lastServers[index].target = target;
+ lastServers[index].time = X->xselectionrequest.time;
+
+ /* Prepare the request (like XConvertSelection, but locally). */
+ xEvent x = {0};
+ x.u.u.type = SelectionRequest;
+ x.u.selectionRequest.time = GetTimeInMillis();
+ x.u.selectionRequest.owner = lastSelectionOwner[index].window;
+ x.u.selectionRequest.selection = CurrentSelections[index].selection;
+ x.u.selectionRequest.property = clientCutProperty;
+ x.u.selectionRequest.requestor = screenInfo.screens[0]->root->drawable.id; /* 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 = lastSelectionOwner[index].window;
+ */
+
+ /*
+ * In TextClipboard mode simply use the previous clipboard
+ * handling code.
+ */
+ if (nxagentOption(TextClipboard))
+ {
+ /* by dimbor */
+ if (target != XA_STRING)
+ {
+ lastServers[index].target = serverUTF8_STRING;
+ /* by dimbor (idea from zahvatov) */
+ x.u.selectionRequest.target = clientUTF8_STRING;
}
else
{
- /* deny the request */
- replyRequestSelection(X, False);
+ x.u.selectionRequest.target = XA_STRING;
}
}
+ else
+ {
+ x.u.selectionRequest.target = nxagentRemoteToLocalAtom(target);
+ }
+
+ /*
+ * Selete property before sending the request to the client as
+ * required by ICCCM.
+ */
+ DeleteProperty(lastSelectionOwner[index].windowPtr, clientCutProperty);
+
+ sendEventToClient(lastSelectionOwner[index].client, &x);
+
+ #ifdef DEBUG
+ fprintf(stderr, "%s: sent SelectionRequest event to client %s property [%d][%s] " \
+ "target [%d][%s] requestor [0x%x] selection [%d][%s].\n", __func__,
+ nxagentClientInfoString(lastSelectionOwner[index].client),
+ x.u.selectionRequest.property, NameForLocalAtom(x.u.selectionRequest.property),
+ x.u.selectionRequest.target, NameForLocalAtom(x.u.selectionRequest.target),
+ x.u.selectionRequest.requestor,
+ x.u.selectionRequest.selection, NameForLocalAtom(x.u.selectionRequest.selection));
+ #endif
+ /* No reply to the Xserver yet - we will do that once the answer
+ of the above sendEventToClient arrives. */
+ }
+ else
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: no local owner for selection [%ld][%s] - denying request.\n", __func__,
+ X->xselectionrequest.selection, NameForRemoteAtom(X->xselectionrequest.selection));
+ #endif
+
+ /* deny the request */
+ replyRequestSelectionToXServer(X, False);
}
}
/*
- * end current selection transfer by sending a notification to the
+ * End the 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.
+ * denied/failed".
+ * Use SELECTION_SUCCESS and SELECTION_FAULT macros for the success
+ * value.
*/
-static void endTransfer(Bool success)
+static void endTransfer(int index, Bool success)
{
- if (lastClientClientPtr == NULL)
+ if (lastClients[index].clientPtr == NULL)
{
#ifdef DEBUG
- fprintf(stderr, "%s: lastClientClientPtr is NULL - doing nothing.\n", __func__);
+ fprintf(stderr, "%s: lastClients[%d].clientPtr is NULL - doing nothing.\n", __func__, index);
#endif
}
else
@@ -1024,56 +1425,64 @@ static void endTransfer(Bool success)
#ifdef DEBUG
if (success == SELECTION_SUCCESS)
fprintf(stderr, "%s: sending notification to client %s, property [%d][%s]\n", __func__,
- nxagentClientInfoString(lastClientClientPtr), lastClientProperty, NameForAtom(lastClientProperty));
+ nxagentClientInfoString(lastClients[index].clientPtr), lastClients[index].property, NameForLocalAtom(lastClients[index].property));
else
fprintf(stderr, "%s: sending negative notification to client %s\n", __func__,
- nxagentClientInfoString(lastClientClientPtr));
+ nxagentClientInfoString(lastClients[index].clientPtr));
#endif
- sendSelectionNotifyEventToClient(lastClientClientPtr,
- lastClientTime,
- lastClientRequestor,
- lastClientSelection,
- lastClientTarget,
- success == SELECTION_SUCCESS ? lastClientProperty : None);
+ sendSelectionNotifyEventToClient(lastClients[index].clientPtr,
+ lastClients[index].time,
+ lastClients[index].requestor,
+ localSelelectionAtoms[index],
+ lastClients[index].target,
+ success == SELECTION_SUCCESS ? lastClients[index].property : None);
}
/*
* Enable further requests from clients.
*/
- setClientSelectionStage(SelectionStageNone);
+ setClientSelectionStage(index, SelectionStageNone);
}
-static void transferSelection(int resource)
+static void transferSelectionFromXServer(int index, int resource)
{
- if (lastClientClientPtr -> index != resource)
+ #ifdef DEBUG
+ fprintf(stderr, "%s: resource [%d] lastClients[%d].clientPtr->index [%d].\n", __func__,
+ resource, index, lastClients[index].clientPtr -> index);
+ #endif
+ /* FIXME: can we use this instead of lastClients[index].resource? */
+ if (lastClients[index].clientPtr -> index != resource)
{
#ifdef DEBUG
fprintf (stderr, "%s: WARNING! Inconsistent resource [%d] with current client %s.\n", __func__,
- resource, nxagentClientInfoString(lastClientClientPtr));
+ resource, nxagentClientInfoString(lastClients[index].clientPtr));
#endif
- endTransfer(SELECTION_FAULT);
+ endTransfer(index, SELECTION_FAULT);
return;
}
- switch (lastClientStage)
+ switch (lastClients[index].stage)
{
case SelectionStageQuerySize:
{
int result;
- printClientSelectionStage();
+ printClientSelectionStage(index);
/*
* Don't get data yet, just get size. We skip this stage in
* current implementation and go straight to the data.
*/
- nxagentLastClipboardClient = NXGetCollectPropertyResource(nxagentDisplay);
+ /* Get next free resource slot. */
+ int free_resource = NXGetCollectPropertyResource(nxagentDisplay);
- if (nxagentLastClipboardClient == -1)
+ lastClients[index].resource = free_resource;
+
+ if (free_resource == -1)
{
#ifdef WARNING
fprintf(stderr, "%s: WARNING! Asynchronous GetProperty queue full.\n", __func__);
@@ -1083,8 +1492,9 @@ static void transferSelection(int resource)
}
else
{
+ /* Collect the property and store it with index "resource" */
result = NXCollectProperty(nxagentDisplay,
- nxagentLastClipboardClient,
+ free_resource,
serverWindow,
serverTransToAgentProperty,
0,
@@ -1097,15 +1507,15 @@ static void transferSelection(int resource)
{
#ifdef DEBUG
fprintf (stderr, "%s: Aborting selection notify procedure for client %s.\n", __func__,
- nxagentClientInfoString(lastClientClientPtr));
+ nxagentClientInfoString(lastClients[index].clientPtr));
#endif
- endTransfer(SELECTION_FAULT);
+ endTransfer(index, SELECTION_FAULT);
return;
}
- setClientSelectionStage(SelectionStageWaitSize);
+ setClientSelectionStage(index, SelectionStageWaitSize);
NXFlushDisplay(nxagentDisplay, NXFlushLink);
@@ -1115,7 +1525,7 @@ static void transferSelection(int resource)
{
int result;
- printClientSelectionStage();
+ printClientSelectionStage(index);
/*
* Request the selection data now.
@@ -1125,9 +1535,12 @@ static void transferSelection(int resource)
fprintf(stderr, "%s: Getting property content from remote server.\n", __func__);
#endif
- nxagentLastClipboardClient = NXGetCollectPropertyResource(nxagentDisplay);
+ /* Get next free resource slot. */
+ resource = NXGetCollectPropertyResource(nxagentDisplay);
+
+ lastClients[index].resource = resource;
- if (nxagentLastClipboardClient == -1)
+ if (resource == -1)
{
#ifdef WARNING
fprintf(stderr, "%s: WARNING! Asynchronous GetProperty queue full.\n", __func__);
@@ -1137,12 +1550,19 @@ static void transferSelection(int resource)
}
else
{
+ /*
+ * Now initiate kind of an asynchronuos GetProperty()
+ * call. Once the property has been retrieved we will
+ * receive an NXCollectPropertyNotify event which will then
+ * be handled in
+ * nxagentCollectPropertyEventFromXServer().
+ */
result = NXCollectProperty(nxagentDisplay,
- nxagentLastClipboardClient,
+ resource,
serverWindow,
serverTransToAgentProperty,
0,
- lastClientPropertySize,
+ lastClients[index].propertySize,
False,
AnyPropertyType);
}
@@ -1151,17 +1571,17 @@ static void transferSelection(int resource)
{
#ifdef DEBUG
fprintf (stderr, "%s: Aborting selection notify procedure for client %s.\n", __func__,
- nxagentClientInfoString(lastClientClientPtr));
+ nxagentClientInfoString(lastClients[index].clientPtr));
#endif
- endTransfer(SELECTION_FAULT);
+ endTransfer(index, SELECTION_FAULT);
return;
}
- setClientSelectionStage(SelectionStageWaitData);
+ setClientSelectionStage(index, SelectionStageWaitData);
- /* we've seen situations where you had to move the mouse or press a
+ /* 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);
@@ -1170,8 +1590,9 @@ static void transferSelection(int resource)
default:
{
#ifdef DEBUG
- fprintf (stderr, "%s: WARNING! Inconsistent state [%s] for client %s.\n", __func__,
- getClientSelectionStageString(lastClientStage), nxagentClientInfoString(lastClientClientPtr));
+ fprintf (stderr, "%s: WARNING! Inconsistent state [%s] for selection [%d] for client %s.\n", __func__,
+ getClientSelectionStageString(lastClients[index].stage), index,
+ nxagentClientInfoString(lastClients[index].clientPtr));
#endif
break;
@@ -1180,13 +1601,15 @@ static void transferSelection(int resource)
}
/*
- Called from Events.c/nxagentHandlePropertyNotify
-
+ Called from Events.c/nxagentHandleCollectPropertyEvent
This event is generated after XChangeProperty(), XDeleteProperty() or
- XGetWindowProperty(delete=True)
-*/
+ XGetWindowProperty(delete=True).
-void nxagentCollectPropertyEvent(int resource)
+ Returncode:
+ True: processed
+ False: not processed, resource is not ours
+*/
+Bool nxagentCollectPropertyEventFromXServer(int resource)
{
XlibAtom atomReturnType;
int resultFormat;
@@ -1194,6 +1617,43 @@ void nxagentCollectPropertyEvent(int resource)
unsigned long ulReturnBytesLeft;
unsigned char *pszReturnData = NULL;
+ int index = 0;
+
+ if (resource < 0)
+ {
+ #ifdef DEBUG
+ fprintf (stderr, "%s: resource [%d] is invalid.\n", __func__, resource);
+ #endif
+ return False;
+ }
+
+ /* Determine the selection we are talking about here. */
+ for (index = 0; index < nxagentMaxSelections; index++)
+ {
+ /*
+ #ifdef DEBUG
+ fprintf(stderr, "%s: lastClients[%d].resource [%d] resource [%d]\n", __func__, index, lastClients[index].resource, resource);
+ #endif
+ */
+ if (lastClients[index].resource == resource)
+ {
+ #ifdef DEBUG
+ fprintf (stderr, "%s: resource [%d] belongs to selection [%d].\n", __func__, resource, index);
+ #endif
+ break;
+ }
+ }
+
+ if (index == nxagentMaxSelections)
+ {
+ /*
+ #ifdef DEBUG
+ fprintf (stderr, "%s: resource [%d] does not belong to any selection we handle.\n", __func__, resource);
+ #endif
+ */
+ return False;
+ }
+
/*
* We have received the notification so we can safely retrieve data
* from the client structure.
@@ -1207,7 +1667,37 @@ void nxagentCollectPropertyEvent(int resource)
&ulReturnBytesLeft,
&pszReturnData);
- nxagentLastClipboardClient = -1;
+ #ifdef DEBUG
+ fprintf(stderr, "%s: NXGetCollectedProperty: result [%d]\n", __func__, result);
+ fprintf(stderr, "%s: atomReturnType [%ld]\n", __func__, atomReturnType);
+ fprintf(stderr, "%s: resultFormat [%d]\n", __func__, resultFormat);
+ fprintf(stderr, "%s: ulReturnItems [%lu]\n", __func__, ulReturnItems);
+ fprintf(stderr, "%s: ulReturnBytesLeft [%lu]\n", __func__, ulReturnBytesLeft);
+ #endif
+
+ lastClients[index].resource = -1;
+
+ /*
+ * ICCCM states: "The requestor must delete the property named in
+ * the SelectionNotify once all the data has been retrieved. The
+ * requestor should invoke either DeleteProperty or GetProperty
+ * (delete==True) after it has successfully retrieved all the data
+ * in the selection."
+ * FIXME: this uses serverTransToAgentProperty which is shared between
+ * all the selections. Could be a problem with simultaneous transfers.
+ * FIXME: NXGetCollectedProperty can return 0 and True. Some other
+ * functions in this field return False as well. Clean up that
+ * mess...
+ */
+ if (result == True && ulReturnBytesLeft == 0)
+ {
+ #ifdef DEBUG
+ fprintf (stderr, "%s: Retrieved property data - deleting property [%ld][%s] "
+ "for ICCCM conformity.\n", __func__, serverTransToAgentProperty,
+ NameForRemoteAtom(serverTransToAgentProperty));
+ #endif
+ XDeleteProperty(nxagentDisplay, serverWindow, serverTransToAgentProperty);
+ }
if (result == 0)
{
@@ -1215,25 +1705,26 @@ void nxagentCollectPropertyEvent(int resource)
fprintf (stderr, "%s: Failed to get reply data.\n", __func__);
#endif
- endTransfer(SELECTION_FAULT);
+ endTransfer(index, SELECTION_FAULT);
}
else if (resultFormat != 8 && resultFormat != 16 && resultFormat != 32)
{
#ifdef DEBUG
- fprintf (stderr, "%s: WARNING! Invalid property format.\n", __func__);
+ fprintf(stderr, "%s: WARNING! Invalid property format [%d].\n", __func__, resultFormat);
#endif
- endTransfer(SELECTION_FAULT);
+ endTransfer(index, SELECTION_FAULT);
}
else
{
- switch (lastClientStage)
+ switch (lastClients[index].stage)
{
case SelectionStageWaitSize:
{
- printClientSelectionStage();
+ printClientSelectionStage(index);
#ifdef DEBUG
- fprintf (stderr, "%s: Got size notify event for client %s.\n", __func__, nxagentClientInfoString(lastClientClientPtr));
+ fprintf (stderr, "%s: Got size notify event for client %s.\n", __func__,
+ nxagentClientInfoString(lastClients[index].clientPtr));
#endif
if (ulReturnBytesLeft == 0)
@@ -1242,7 +1733,7 @@ void nxagentCollectPropertyEvent(int resource)
fprintf (stderr, "%s: data size is [0] - aborting selection notify procedure.\n", __func__);
#endif
- endTransfer(SELECTION_FAULT);
+ endTransfer(index, SELECTION_FAULT);
}
else
{
@@ -1253,18 +1744,19 @@ void nxagentCollectPropertyEvent(int resource)
/*
* Request the selection data now.
*/
- lastClientPropertySize = ulReturnBytesLeft;
- setClientSelectionStage(SelectionStageQueryData);
+ lastClients[index].propertySize = ulReturnBytesLeft;
+ setClientSelectionStage(index, SelectionStageQueryData);
- transferSelection(resource);
+ transferSelectionFromXServer(index, resource);
}
break;
}
case SelectionStageWaitData:
{
- printClientSelectionStage();
+ printClientSelectionStage(index);
#ifdef DEBUG
- fprintf (stderr, "%s: Got data notify event for waiting client %s.\n", __func__, nxagentClientInfoString(lastClientClientPtr));
+ fprintf (stderr, "%s: Got data notify event for waiting client %s.\n", __func__,
+ nxagentClientInfoString(lastClients[index].clientPtr));
#endif
if (ulReturnBytesLeft != 0)
@@ -1273,43 +1765,103 @@ void nxagentCollectPropertyEvent(int resource)
fprintf (stderr, "%s: not all content could be retrieved - [%lu] bytes left - aborting selection notify procedure.\n", __func__, ulReturnBytesLeft);
#endif
- endTransfer(SELECTION_FAULT);
+ endTransfer(index, SELECTION_FAULT);
}
else
{
#ifdef DEBUG
- fprintf(stderr, "%s: Got property content from remote server. size [%lu] bytes.\n", __func__, (ulReturnItems * resultFormat / 8));
+ fprintf(stderr, "%s: Got property content from remote server. [%lu] items with format [%d] = [%lu] bytes.\n", __func__, ulReturnItems, resultFormat, (ulReturnItems * resultFormat/8));
#endif
- ChangeWindowProperty(lastClientWindowPtr,
- lastClientProperty,
- lastClientTarget,
- resultFormat, PropModeReplace,
- ulReturnItems, pszReturnData, 1);
+ if (lastClients[index].target == clientTARGETS)
+ {
+ Atom * targets = calloc(sizeof(Atom), ulReturnItems);
+ if (targets == NULL)
+ {
+ #ifdef WARNING
+ fprintf(stderr, "%s: WARNING! Could not alloc memory for clipboard targets transmission.\n", __func__);
+ #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
+ /* operation failed */
+ endTransfer(index, SELECTION_FAULT);
+ }
+ else
+ {
+ /* fprintf(stderr, "sizeof(Atom) [%lu], sizeof(XlibAtom) [%lu], sizeof(long) [%lu], sizeof(CARD32) [%lu] sizeof(INT32) [%lu]\n", sizeof(Atom), sizeof(XlibAtom), sizeof(long), sizeof(CARD32), sizeof(INT32)); */
+
+ Atom *addr = targets;
+ unsigned int numTargets = ulReturnItems;
+
+ for (int i = 0; i < numTargets; i++)
+ {
+ XlibAtom remote = *((XlibAtom*)(pszReturnData + i*resultFormat/8));
+ Atom local = nxagentRemoteToLocalAtom(remote);
+ *(addr++) = local;
+
+ #ifdef DEBUG
+ fprintf(stderr, "%s: converting atom: remote [%u][%s] -> local [%u][%s]\n", __func__,
+ (unsigned int)remote, NameForRemoteAtom(remote), local, NameForLocalAtom(local));
+ #endif
+ }
+ ChangeWindowProperty(lastClients[index].windowPtr,
+ lastClients[index].property,
+ MakeAtom("ATOM", 4, 1),
+ 32, PropModeReplace,
+ ulReturnItems, (unsigned char*)targets, 1);
+
+ cacheTargetsForLocal(index, targets, numTargets);
+
+ endTransfer(index, SELECTION_SUCCESS);
+ }
+ }
+ else
+ {
+ ChangeWindowProperty(lastClients[index].windowPtr,
+ lastClients[index].property,
+ nxagentRemoteToLocalAtom(atomReturnType),
+ resultFormat, PropModeReplace,
+ ulReturnItems, pszReturnData, 1);
+
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Selection property [%d][%s] changed to resultFormat [%d] returnType [%ld][%s] len [%d]"
+ #ifdef PRINT_CLIPBOARD_CONTENT_ON_DEBUG
+ /* FIXME: only print the string if the resultFormat is 8 */
+ " value [\"%*.*s\"...] hex [0x%2.2x%2.2x%2.2x%2.2x]"
+ #endif
+ "\n", __func__,
+ lastClients[index].property,
+ validateString(NameForLocalAtom(lastClients[index].property)),
+ resultFormat,
+ atomReturnType, NameForRemoteAtom(atomReturnType),
+ (int)ulReturnItems * resultFormat / 8
+ #ifdef PRINT_CLIPBOARD_CONTENT_ON_DEBUG
+ ,(int)(min(20, ulReturnItems * resultFormat / 8)),
+ (int)(min(20, ulReturnItems * resultFormat / 8)),
+ pszReturnData,
+ pszReturnData[0], pszReturnData[1], pszReturnData[2], pszReturnData[3]
+ #endif
+ );
+ #endif
- endTransfer(SELECTION_SUCCESS);
+ endTransfer(index, SELECTION_SUCCESS);
+ }
}
break;
}
default:
{
#ifdef DEBUG
- fprintf (stderr, "%s: WARNING! Inconsistent state [%s] for client %s.\n", __func__,
- getClientSelectionStageString(lastClientStage), nxagentClientInfoString(lastClientClientPtr));
+ fprintf(stderr, "%s: WARNING! Inconsistent state [%s] for client %s.\n", __func__,
+ getClientSelectionStageString(lastClients[index].stage),
+ nxagentClientInfoString(lastClients[index].clientPtr));
#endif
break;
}
}
}
SAFE_XFree(pszReturnData);
+
+ return True;
}
/*
@@ -1327,23 +1879,39 @@ void nxagentHandleSelectionNotifyFromXServer(XEvent *X)
return;
}
+ XSelectionEvent *E = (XSelectionEvent *)X;
#ifdef DEBUG
+ fprintf(stderr, "---------\n%s: Received SelectionNotify event from real X server, property " \
+ "[%ld][%s] requestor [0x%lx] selection [%s] target [%ld][%s] time [%lu] send_event [%d].\n",
+ __func__, E->property, NameForRemoteAtom(E->property), E->requestor,
+ NameForRemoteAtom(E->selection), E->target,
+ NameForRemoteAtom(E->target), E->time, E->send_event);
+
+ /* this has not been SENT by nxagent but is the answer to a request of nxagent */
+ if (E->requestor == serverWindow)
{
- 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);
+ fprintf(stderr, "%s: requestor is nxagent's serverWindow!\n", __func__);;
}
#endif
- printClientSelectionStage();
+ /* determine the selection we are talking about here */
+ int index = nxagentFindRemoteSelectionIndex(E->selection);
+ if (index == -1)
+ {
+ #ifdef DEBUG
+ fprintf (stderr, "%s: unknown selection [%ld] .\n", __func__, E->selection);
+ #endif
+ return;
+ }
+
+ printClientSelectionStage(index);
+
+ /*
+ * If the property is serverTransFromAgentProperty this means we are
+ * transferring data FROM the agent TO the server.
+ */
- if (lastClientWindowPtr != NULL)
+ if (X->xselection.property != serverTransFromAgentProperty && lastClients[index].windowPtr != NULL)
{
/*
* We reach here after a paste inside the nxagent, triggered by
@@ -1351,156 +1919,261 @@ void nxagentHandleSelectionNotifyFromXServer(XEvent *X)
* means that data we need has been transferred to the
* serverTransToAgentProperty 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.
+ * requestor, which is stored in the lastClients[index].* variables.
*/
- if (lastClientStage == SelectionStageNone &&
- X->xselection.property == serverTransToAgentProperty)
+
+ #ifdef DEBUG
+ nxagentDumpClipboardStat();
+ #endif
+ if (lastClients[index].stage == SelectionStageNone)
{
- #ifdef DEBUG
- fprintf(stderr, "%s: Starting selection transferral for client %s.\n", __func__,
- nxagentClientInfoString(lastClientClientPtr));
- #endif
+ if (X->xselection.property == serverTransToAgentProperty)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Starting selection transferral for client %s.\n", __func__,
+ nxagentClientInfoString(lastClients[index].clientPtr));
+ #endif
- /*
- * The state machine is able to work in two phases. In the first
- * phase we get the size of property data, in the second we get
- * the actual data. We save a round-trip by requesting a prede-
- * termined amount of data in a single GetProperty and by discar-
- * ding the remaining part. This is not the optimal solution (we
- * could get the remaining part if it doesn't fit in a single
- * reply) but, at least with text, it should work in most situa-
- * tions.
- */
+ /*
+ * The state machine is able to work in two phases. In the first
+ * phase we get the size of property data, in the second we get
+ * the actual data. We save a round-trip by requesting a prede-
+ * termined amount of data in a single GetProperty and by discar-
+ * ding the remaining part. This is not the optimal solution (we
+ * could get the remaining part if it doesn't fit in a single
+ * reply) but, at least with text, it should work in most situa-
+ * tions.
+ */
- setClientSelectionStage(SelectionStageQueryData);
- lastClientPropertySize = 262144;
+ setClientSelectionStage(index, SelectionStageQueryData);
+ lastClients[index].propertySize = 262144;
- transferSelection(lastClientClientPtr -> index);
+ transferSelectionFromXServer(index, lastClients[index].clientPtr -> index);
+ }
+ else if (X->xselection.property == 0)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: WARNING! Resetting selection transferral for client [%d] because of failure notification from real X server.\n", __func__,
+ CLINDEX(lastClients[index].clientPtr));
+ #endif
+ endTransfer(index, SELECTION_FAULT);
+ }
+ else
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Unexpected property [%ld][%s] - reporting conversion failure.\n",
+ __func__, X->xselection.property, NameForRemoteAtom(X->xselection.property));
+ #endif
+ endTransfer(index, SELECTION_FAULT);
+ }
}
else
{
#ifdef DEBUG
- fprintf(stderr, "%s: WARNING! Resetting selection transferral for client [%d].\n", __func__,
- CLINDEX(lastClientClientPtr));
+ fprintf(stderr, "%s: WARNING! Resetting selection transferral for client [%d] because of unexpected stage.\n", __func__,
+ CLINDEX(lastClients[index].clientPtr));
#endif
- endTransfer(SELECTION_FAULT);
+ endTransfer(index, SELECTION_FAULT);
}
}
else
{
- int i = nxagentFindLastSelectionOwnerIndex(X->xselection.selection);
- if (i < nxagentMaxSelections)
- {
- /* if the last owner was an internal one, read the
- * clientCutProperty and push the contents to the
- * lastServerRequestor on the real X server.
- */
- if (IS_INTERNAL_OWNER(i) &&
- lastSelectionOwner[i].windowPtr != NULL &&
- X->xselection.property == serverTransFromAgentProperty)
- {
- Atom atomReturnType;
- int resultFormat;
- unsigned long ulReturnItems;
- unsigned long ulReturnBytesLeft;
- unsigned char *pszReturnData = NULL;
+ handlePropertyTransferFromAgentToXserver(index, X->xselection.property);
+ }
+}
+
+void handlePropertyTransferFromAgentToXserver(int index, XlibAtom property)
+{
+ /*
+ * If the last owner was a local one, read the
+ * clientCutProperty and push the contents to the
+ * lastServers[index].requestor on the real X server.
+ */
+ if (IS_LOCAL_OWNER(index) &&
+ lastSelectionOwner[index].windowPtr != NULL &&
+ property == serverTransFromAgentProperty)
+ {
+ Atom atomReturnType;
+ int resultFormat;
+ unsigned long ulReturnItems;
+ 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);
+ /* First get size values ... */
+ int result = GetWindowProperty(lastSelectionOwner[index].windowPtr, clientCutProperty, 0, 0, False,
+ AnyPropertyType, &atomReturnType, &resultFormat,
+ &ulReturnItems, &ulReturnBytesLeft, &pszReturnData);
- #ifdef DEBUG
- 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)
- {
- lastServerProperty = None;
- }
- else
+ #ifdef DEBUG
+ fprintf(stderr, "%s: GetWindowProperty() window [0x%x] property [%d][%s] returned [%s]\n", __func__,
+ lastSelectionOwner[index].window, clientCutProperty, NameForLocalAtom(clientCutProperty),
+ getXErrorString(result));
+ #endif
+ if (result == BadAlloc || result == BadAtom ||
+ result == BadWindow || result == BadValue)
+ {
+ lastServers[index].property = None;
+ }
+ else
+ {
+ /* ... then use the size values for the actual request data. */
+ result = GetWindowProperty(lastSelectionOwner[index].windowPtr, clientCutProperty, 0,
+ ulReturnBytesLeft, False, AnyPropertyType, &atomReturnType,
+ &resultFormat, &ulReturnItems, &ulReturnBytesLeft,
+ &pszReturnData);
+ #ifdef DEBUG
+ fprintf(stderr, "%s: GetWindowProperty() window [0x%x] property [%d][%s] returned [%s]\n", __func__,
+ lastSelectionOwner[index].window, clientCutProperty, NameForLocalAtom(clientCutProperty),
+ getXErrorString(result));
+ #endif
+
+ if (result == BadAlloc || result == BadAtom ||
+ result == BadWindow || result == BadValue)
+ {
+ lastServers[index].property = None;
+ }
+ else
+ {
+ if (lastServers[index].target == serverTARGETS)
{
- /* ... 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() window [0x%x] property [%d] returned [%s]\n", __func__,
- lastSelectionOwner[i].window, clientCutProperty, getXErrorString(result));
+ fprintf(stderr, "%s: ulReturnItems [%ld]\n", __func__, ulReturnItems);
+ fprintf(stderr, "%s: resultformat [%d]\n", __func__, resultFormat);
#endif
- if (result == BadAlloc || result == BadAtom ||
- result == BadWindow || result == BadValue)
+ XlibAtom * targets = calloc(sizeof(XlibAtom), ulReturnItems);
+ if (targets == NULL)
{
- lastServerProperty = None;
+ #ifdef WARNING
+ fprintf(stderr, "%s: WARNING! Could not alloc memory for clipboard targets transmission.\n", __func__);
+ #endif
+ /* This will effectively lead to the request being answered as failed. */
+ lastServers[index].property = None;
}
else
{
- /* 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);
+ /* Convert the targets to remote atoms. */
+ XlibAtom *addr = targets;
+ unsigned int numTargets = ulReturnItems;
- #ifdef DEBUG
+ for (int i = 0; i < numTargets; i++)
{
- char *s = XGetAtomName(nxagentDisplay, lastServerProperty);
- fprintf(stderr, "%s: XChangeProperty sent to window [0x%x] for property [%ld][%s] value [\"%*.*s\"...]\n",
- __func__,
- lastServerRequestor,
- lastServerProperty,
- s,
- (int)(min(20, ulReturnItems * 8 / 8)),
- (int)(min(20, ulReturnItems * 8 / 8)),
- pszReturnData);
- SAFE_XFree(s);
+ Atom local = *((Atom*)(pszReturnData + i*resultFormat/8));
+ XlibAtom remote = nxagentLocalToRemoteAtom(local);
+ *(addr++) = remote;
+
+ #ifdef DEBUG
+ fprintf(stderr, "%s: converting atom: local [%d][%s] -> remote [%ld][%s]\n", __func__,
+ local, NameForLocalAtom(local), remote, NameForRemoteAtom(remote));
+ #endif
}
- #endif
- }
- /* FIXME: free it or not? */
- /*
- * SAFE_XFree(pszReturnData);
- */
+ /* FIXME: do we need to take care of swapping byte order here? */
+ XChangeProperty(nxagentDisplay,
+ lastServers[index].requestor,
+ lastServers[index].property,
+ XInternAtom(nxagentDisplay, "ATOM", 0),
+ 32,
+ PropModeReplace,
+ (unsigned char*)targets,
+ numTargets);
+
+ cacheTargetsForRemote(index, targets, numTargets);
+ }
+ }
+ else
+ {
+ /* Fill the property on the 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 */
+ XChangeProperty(nxagentDisplay,
+ lastServers[index].requestor,
+ lastServers[index].property,
+ nxagentLocalToRemoteAtom(atomReturnType),
+ resultFormat,
+ PropModeReplace,
+ pszReturnData,
+ ulReturnItems);
+ #ifdef DEBUG
+ {
+ fprintf(stderr, "%s: XChangeProperty sent to window [0x%lx] for property [%ld][%s] resultFormat [%d] returnType [%ld][%s] len [%d]"
+ #ifdef PRINT_CLIPBOARD_CONTENT_ON_DEBUG
+ /* FIXME: only print the string if the resultFormat is 8 */
+ " value [\"%*.*s\"...] hex [0x%2.2x%2.2x%2.2x%2.2x]"
+ #endif
+ "\n",
+ __func__,
+ lastServers[index].requestor,
+ lastServers[index].property,
+ NameForRemoteAtom(lastServers[index].property),
+ resultFormat,
+ nxagentLocalToRemoteAtom(atomReturnType), NameForLocalAtom(atomReturnType),
+ (int)ulReturnItems * resultFormat / 8
+ #ifdef PRINT_CLIPBOARD_CONTENT_ON_DEBUG
+ ,(int)(min(20, ulReturnItems * 8 / 8)),
+ (int)(min(20, ulReturnItems * 8 / 8)),
+ pszReturnData,
+ pszReturnData[0], pszReturnData[1], pszReturnData[2], pszReturnData[3]
+ #endif
+ );
+ }
+ #endif
}
+ /* FIXME: free it or not? */
/*
- * 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.
+ * SAFE_XFree(pszReturnData);
*/
- 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 SelectionNotify event to requestor [%p].\n", __func__,
- (void *)eventSelection.requestor);
- #endif
-
- sendSelectionNotifyEventToServer(&eventSelection);
-
- lastServerRequestor = None; /* allow further request */
}
}
+
+ /*
+ * 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 lastServers[index].property will be None which
+ * effectively will send a "Request denied" to the initial
+ * requestor.
+ */
+ replyPendingRequestSelectionToXServer(index, True);
+ }
+}
+
+/*
+ * This is similar to replyRequestSelectionToXServer(), but gets the
+ * required values from a stored request instead of an XEvent
+ * structure.
+ */
+void replyPendingRequestSelectionToXServer(int index, Bool success)
+{
+ if (lastServers[index].requestor == None)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: no pending request for index [%d] - doing nothing\n", __func__, index);
+ #endif
+ }
+ else
+ {
+ XSelectionEvent eventSelection = {
+ .requestor = lastServers[index].requestor,
+ .selection = remoteSelectionAtoms[index],
+ .target = lastServers[index].target,
+ .time = lastServers[index].time,
+ .property = success ? lastServers[index].property : None,
+ };
+
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Sending %s SelectionNotify event to requestor [%p].\n", __func__,
+ success ? "positive" : "negative", (void *)eventSelection.requestor);
+ #endif
+
+ sendSelectionNotifyEventToXServer(&eventSelection);
+
+ lastServers[index].requestor = None; /* allow further request */
+ lastServers[index].property = 0;
+ lastServers[index].target = 0;
+ lastServers[index].time = 0;
}
}
@@ -1511,127 +2184,147 @@ void nxagentHandleSelectionNotifyFromXServer(XEvent *X)
* the real X server (or its clients) will be sent to this window. The
* real X server never communicates with our windows directly.
*/
-static void resetSelectionOwner(void)
+static void resetSelectionOwnerOnXServer(void)
{
- if (lastServerRequestor != None)
+ if (lastServers[index].requestor != None)
{
/*
- * we are in the process of communicating back and forth between
+ * 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);
+ fprintf(stderr, "%s: WARNING! Requestor window [0x%x] already set.\n", __func__,
+ lastServers[index].requestor);
#endif
/* FIXME: maybe we should put back the event that lead us here. */
return;
}
- /*
- * Only for PRIMARY and CLIPBOARD selections.
- */
-
- for (int i = 0; i < nxagentMaxSelections; i++)
+ for (int index = 0; index < nxagentMaxSelections; index++)
{
- XSetSelectionOwner(nxagentDisplay, lastSelectionOwner[i].selection, serverWindow, CurrentTime);
+ XSetSelectionOwner(nxagentDisplay, remoteSelectionAtoms[index], serverWindow, CurrentTime);
#ifdef DEBUG
- fprintf(stderr, "%s: Reset selection state for selection [%d].\n", __func__, i);
+ fprintf(stderr, "%s: Reset selection state for selection [%d].\n", __func__, index);
#endif
- clearSelectionOwner(i);
- }
+ clearSelectionOwnerData(index);
- setClientSelectionStage(SelectionStageNone);
+ setClientSelectionStage(index, SelectionStageNone);
- /* Hmm, this is already None when reaching this */
- lastServerRequestor = None;
+ invalidateTargetCache(index);
+
+ /* Hmm, this is already None when reaching here. */
+ lastServers[index].requestor = None;
+ }
}
#endif
#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.
+ * The callback is called from dix.
*/
-
void nxagentSetSelectionCallback(CallbackListPtr *callbacks, void *data,
void *args)
{
- /*
- * 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
- * could get rid of the Trap...
- */
- if (nxagentExternalClipboardEventTrap)
+ SelectionInfoRec *info = (SelectionInfoRec *)args;
+
+ #ifdef DEBUG
+ fprintf(stderr, "---------\n");
+ if (info->kind == SelectionSetOwner)
+ {
+ fprintf(stderr, "%s: SelectionCallbackKind [SelectionSetOwner]\n", __func__);
+ }
+ else if (info->kind == SelectionWindowDestroy)
+ {
+ fprintf(stderr, "%s: SelectionCallbackKind [SelectionWindowDestroy]\n", __func__);
+ }
+ else if (info->kind == SelectionClientClose)
+ {
+ fprintf(stderr, "%s: SelectionCallbackKind [SelectionClientClose]\n", __func__);
+ }
+ else
+ {
+ fprintf(stderr, "%s: SelectionCallbackKind [unknown]\n", __func__);
+ }
+ #endif
+
+ Selection * pCurSel = (Selection *)info->selection;
+
+ int index = nxagentFindCurrentSelectionIndex(pCurSel->selection);
+ if (index == -1)
{
#ifdef DEBUG
- fprintf(stderr, "%s: Trap is set, doing nothing\n", __func__);
+ fprintf(stderr, "%s: selection [%s] can/will not be handled by the clipboard code\n", __func__, NameForLocalAtom(pCurSel->selection));
#endif
return;
}
- SelectionInfoRec *info = (SelectionInfoRec *)args;
-
- Selection * pCurSel = (Selection *)info->selection;
+ /*
+ * Always invalidate the target cache for the relevant selection.
+ * This ensures not having invalid data in the cache.
+ */
+ invalidateTargetCache(index);
#ifdef DEBUG
+ fprintf(stderr, "%s: pCurSel->window [0x%x]\n", __func__, pCurSel->window);
+ fprintf(stderr, "%s: pCurSel->pWin [0x%x]\n", __func__, WINDOWID(pCurSel->pWin));
+ fprintf(stderr, "%s: pCurSel->selection [%s]\n", __func__, NameForLocalAtom(pCurSel->selection));
fprintf(stderr, "%s: pCurSel->lastTimeChanged [%u]\n", __func__, pCurSel->lastTimeChanged.milliseconds);
+ fprintf(stderr, "%s: pCurSel->client [%s]\n", __func__, nxagentClientInfoString(pCurSel->client));
#endif
- if (info->kind == SelectionSetOwner)
+ if (nxagentOption(Clipboard) != ClipboardNone) /* FIXME: shouldn't we also check for != ClipboardClient? */
{
- #ifdef DEBUG
- fprintf(stderr, "%s: called with SelectionCallbackKind SelectionSetOwner\n", __func__);
- fprintf(stderr, "%s: pCurSel->pWin [0x%x]\n", __func__, WINDOWID(pCurSel->pWin));
- fprintf(stderr, "%s: pCurSel->selection [%s]\n", __func__, NameForAtom(pCurSel->selection));
- #endif
+ Selection *pSel = NULL;
+ Selection nullSel = {
+ .client = NullClient,
+ .window = None,
+ .pWin = NULL,
+ .selection = pCurSel->selection,
+ .lastTimeChanged = pCurSel->lastTimeChanged
+ };
- if (pCurSel->pWin != NULL &&
- nxagentOption(Clipboard) != ClipboardNone && /* FIXME: shouldn't we also check for != ClipboardClient? */
- (pCurSel->selection == XA_PRIMARY ||
- pCurSel->selection == clientCLIPBOARD))
+ if (info->kind == SelectionSetOwner)
+ {
+ pSel = pCurSel;
+ }
+ else if (info->kind == SelectionWindowDestroy)
+ {
+ if (pCurSel->window == lastSelectionOwner[index].window)
+ {
+ pSel = &nullSel;
+ }
+ }
+ else if (info->kind == SelectionClientClose)
+ {
+ if (pCurSel->client == lastSelectionOwner[index].client)
+ {
+ pSel = &nullSel;
+ }
+ }
+ else
+ {
+ }
+
+ if (pSel)
{
#ifdef DEBUG
- fprintf(stderr, "%s: calling setSelectionOwner\n", __func__);
+ fprintf(stderr, "%s: calling setSelectionOwnerOnXServer\n", __func__);
#endif
- setSelectionOwner(pCurSel);
+ setSelectionOwnerOnXServer(pSel);
}
}
- else if (info->kind == SelectionWindowDestroy)
- {
- #ifdef DEBUG
- fprintf(stderr, "%s: called with SelectionCallbackKind SelectionWindowDestroy\n", __func__);
- #endif
- }
- else if (info->kind == SelectionClientClose)
- {
- #ifdef DEBUG
- fprintf(stderr, "%s: called with SelectionCallbackKind SelectionClientClose\n", __func__);
- #endif
- }
- else
- {
- #ifdef DEBUG
- fprintf(stderr, "%s: called with unknown SelectionCallbackKind\n", __func__);
- #endif
- }
}
#endif
/*
* This is called from the nxagentSetSelectionCallback, so it is using
- * internal Atoms
+ * local Atoms.
*/
-static void setSelectionOwner(Selection *pSelection)
+static void setSelectionOwnerOnXServer(Selection *pSelection)
{
if (!agentClipboardInitialized)
{
@@ -1641,81 +2334,102 @@ static void setSelectionOwner(Selection *pSelection)
return;
}
- #ifdef DEBUG
- fprintf(stderr, "%s: Setting selection owner to serverwindow ([0x%x]).\n", __func__,
- serverWindow);
- #endif
-
- #if defined(TEST) || defined(DEBUG)
- if (lastServerRequestor != None)
+ int index = nxagentFindCurrentSelectionIndex(pSelection->selection);
+ if (index == -1)
{
- /*
- * 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);
+ #ifdef DEBUG
+ fprintf(stderr, "%s: selection [%s] can/will not be handled by the clipboard code\n", __func__, NameForLocalAtom(pSelection->selection));
+ #endif
+ return;
}
+
+ #ifdef DEBUG
+ fprintf(stderr, "%s: lastSelectionOwner.client %s -> %s\n", __func__,
+ nxagentClientInfoString(lastSelectionOwner[index].client),
+ nxagentClientInfoString(pSelection->client));
+ fprintf(stderr, "%s: lastSelectionOwner.window [0x%x] -> [0x%x]\n", __func__,
+ lastSelectionOwner[index].window, pSelection->window);
+ fprintf(stderr, "%s: lastSelectionOwner.windowPtr [%p] -> [%p] [0x%x] (serverWindow: [0x%lx])\n", __func__,
+ (void *)lastSelectionOwner[index].windowPtr, (void *)pSelection->pWin,
+ pSelection->pWin ? nxagentWindow(pSelection->pWin) : 0, serverWindow);
+ fprintf(stderr, "%s: lastSelectionOwner.lastTimeChanged [%u]\n", __func__,
+ lastSelectionOwner[index].lastTimeChanged);
#endif
- int i = nxagentFindCurrentSelectionIndex(pSelection->selection);
- if (i < NumCurrentSelections)
- {
- #ifdef DEBUG
- fprintf(stderr, "%s: lastSelectionOwner.client %s -> %s\n", __func__,
- nxagentClientInfoString(lastSelectionOwner[i].client),
- nxagentClientInfoString(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.
- */
- XSetSelectionOwner(nxagentDisplay, lastSelectionOwner[i].selection, serverWindow, CurrentTime);
+ /*
+ * There's an X client on the real X server waiting for a
+ * reply. That reply will never come because now we are the
+ * owner so let's be fair and cancel that request.
+ */
+ replyPendingRequestSelectionToXServer(index, False);
- /*
- * 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.
- */
- storeSelectionOwner(i, pSelection);
+ /*
+ * Inform the real X server that our serverWindow is the
+ * clipboard owner.
+ * https://www.freedesktop.org/wiki/ClipboardManager/ states
+ * "In order to support peers who use the XFIXES extension to
+ * watch clipboard ownership changes, clipboard owners should
+ * reacquire the clipboard whenever the content or metadata (e.g
+ * the list of supported targets) changes."
+ * If pWin is NULL this is a SelectionClear.
+ */
+ #ifdef DEBUG
+ if (pSelection->pWin)
+ {
+ fprintf(stderr, "%s: Setting selection owner to serverwindow ([0x%lx]).\n", __func__,
+ serverWindow);
}
+ #endif
+ XSetSelectionOwner(nxagentDisplay, remoteSelectionAtoms[index], pSelection->pWin ? serverWindow : 0, CurrentTime);
- setClientSelectionStage(SelectionStageNone);
+ /*
+ * The real owner window (inside nxagent) is stored in
+ * lastSelectionOwner[index].window.
+ * lastSelectionOwner[index].windowPtr points to the struct that
+ * contains all information about the owner window.
+ */
+ storeSelectionOwnerData(index, pSelection);
- lastServerRequestor = None;
+ setClientSelectionStage(index, SelectionStageNone);
+
+ /*
+ * This will be repeated on reception of the SelectionOwner
+ * callback but we cannot be sure if there are any intermediate
+ * requests in the queue already so better do it here, too.
+ */
+ invalidateTargetCache(index);
/*
FIXME
- if (XGetSelectionOwner(nxagentDisplay,pSelection->selection) == serverWindow)
- {
- fprintf (stderr, "%s: SetSelectionOwner OK\n", __func__);
+FIXME2: instead of XGetSelectionOwner we could check if the Xfixes
+ SetSelectionOwner event has arrived in the event queue;
+ possibly saving one roundtrip.
- lastSelectionOwnerSelection = pSelection;
- lastSelectionOwnerClient = pSelection->client;
- lastSelectionOwnerWindow = pSelection->window;
- lastSelectionOwnerWindowPtr = pSelection->pWin;
+ if (XGetSelectionOwner(nxagentDisplay, pSelection->selection) == serverWindow)
+ {
+ fprintf (stderr, "%s: SetSelectionOwner OK\n", __func__);
- setClientSelectionStage(SelectionStageNone);
+ lastSelectionOwnerSelection = pSelection;
+ lastSelectionOwnerClient = pSelection->client;
+ lastSelectionOwnerWindow = pSelection->window;
+ lastSelectionOwnerWindowPtr = pSelection->pWin;
- lastServerRequestor = None;
- }
- else fprintf (stderr, "%s: SetSelectionOwner failed\n", __func__);
+ setClientSelectionStage(index, SelectionStageNone);
+
+ lastServers[index].requestor = None;
+ }
+ else
+ {
+ fprintf (stderr, "%s: SetSelectionOwner failed\n", __func__);
+ }
*/
}
/*
* This is called from dix (ProcConvertSelection) if an nxagent client
- * issues a ConvertSelection request. So all the Atoms are internal
+ * issues a ConvertSelection request. So all the Atoms are local.
* return codes:
* 0: let dix process the request
* 1: don't let dix process the request
@@ -1723,6 +2437,23 @@ FIXME
int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
Window requestor, Atom property, Atom target, Time time)
{
+ #ifdef DEBUG
+ fprintf(stderr, "---------\n%s: client %s requests sel [%s] "
+ "on window [0x%x] prop [%d][%s] target [%d][%s] time [%u].\n", __func__,
+ nxagentClientInfoString(client), NameForLocalAtom(selection), requestor,
+ property, NameForLocalAtom(property),
+ target, NameForLocalAtom(target), time);
+ #endif
+
+ /* We cannot use NameForLocalAtom() here! FIXME: Why not? */
+ if (NameForAtom(target) == NULL)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: cannot find name for target Atom [%d] - returning\n", __func__, target);
+ #endif
+ return 1;
+ }
+
if (!agentClipboardInitialized)
{
#ifdef DEBUG
@@ -1739,105 +2470,173 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
return 0;
}
+ int index = nxagentFindCurrentSelectionIndex(selection);
+ if (index == -1)
{
- int i = nxagentFindCurrentSelectionIndex(selection);
- if (i < NumCurrentSelections && IS_INTERNAL_OWNER(i))
- {
- /*
- * There is a client owner on the agent side, let normal dix stuff happen.
- */
- return 0;
- }
+ #ifdef DEBUG
+ fprintf(stderr, "%s: cannot find index for selection [%u]\n", __func__, selection);
+ #endif
+ return 0;
+ }
+
+ if (IS_LOCAL_OWNER(index))
+ {
+ /*
+ * There is a client owner on the agent side, let normal dix stuff happen.
+ */
+ #ifdef DEBUG
+ fprintf(stderr, "%s: clipboard is owned by local client - let dix process the request\n", __func__);
+ #endif
+ return 0;
}
/*
- * if lastClientWindowPtr is set we are waiting for an answer from
- * the real X server. If that answer takes more than 5 seconds we
- * consider the conversion failed and tell our client about that.
- * The new request that lead us here is then processed.
+ * If lastClients[index].windowPtr is set we are waiting for an
+ * answer from the real X server. If that answer takes more than 5
+ * seconds we consider the conversion failed and tell our client
+ * about that. The new request that lead us here is then processed.
*/
- if (lastClientWindowPtr != NULL)
+ #ifdef TEST
+ fprintf(stderr, "%s: lastClients[%d].windowPtr [0x%lx].\n", __func__, index, (unsigned long)lastClients[index].windowPtr);
+ #endif
+
+ if (lastClients[index].windowPtr != NULL)
{
#ifdef TEST
- fprintf(stderr, "%s: lastClientWindowPtr != NULL.\n", __func__);
+ fprintf(stderr, "%s: lastClients[%d].windowPtr != NULL.\n", __func__, index);
#endif
- if ((GetTimeInMillis() - lastClientReqTime) >= CONVERSION_TIMEOUT)
+ #ifdef DEBUG
+ fprintf(stderr, "%s: localSelelectionAtoms[%d] [%d] - selection [%d]\n", __func__, index, localSelelectionAtoms[index], selection);
+ #endif
+
+ if ((GetTimeInMillis() - lastClients[index].reqTime) >= CONVERSION_TIMEOUT)
{
#ifdef DEBUG
- fprintf(stderr, "%s: timeout expired on last request, "
- "notifying failure to client %s\n", __func__, nxagentClientInfoString(client));
+ fprintf(stderr, "%s: timeout expired on previous request - "
+ "notifying failure to waiting client\n", __func__);
#endif
- endTransfer(SELECTION_FAULT);
+ /* Notify the waiting client of the failure. */
+ endTransfer(index, SELECTION_FAULT);
+
+ /* Do NOT return here but process the new request instead! */
}
else
{
/*
- * we got another convert request while already waiting for an
- * answer from the real X server to a previous convert request,
- * which we cannot handle (yet). So return an error.
+ * We got another convert request while already waiting for an
+ * answer from the real X server to a previous convert request
+ * for this selection, which we cannot handle (yet). So return
+ * an error for the new request.
*/
#ifdef DEBUG
- fprintf(stderr, "%s: got request "
- "before timeout expired on last request, notifying failure to client %s\n",
+ fprintf(stderr, "%s: got new request "
+ "before timeout expired on previous request, notifying failure to client %s\n",
__func__, nxagentClientInfoString(client));
#endif
+ /* Notify the sender of the new request of the failure. */
sendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
return 1;
}
}
- #ifdef DEBUG
- fprintf(stderr, "%s: client %s requests sel [%s] "
- "on window [%x] prop [%d][%s] target [%d][%s].\n", __func__,
- nxagentClientInfoString(client), validateString(NameForAtom(selection)), requestor,
- property, validateString(NameForAtom(property)),
- target, validateString(NameForAtom(target)));
- #endif
-
- 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.
- *
- * The list is aligned with the one in nxagentHandleSelectionRequestFromXServer.
*/
+
if (target == clientTARGETS)
{
- Atom targets[] = {XA_STRING, clientUTF8_STRING, clientTEXT, clientCOMPOUND_TEXT, clientTARGETS, clientTIMESTAMP};
- int numTargets = sizeof(targets) / sizeof(targets[0]);
+ /*
+ * In TextClipboard mode answer with a predefined list that was used
+ * in previous versions.
+ */
+ if (nxagentOption(TextClipboard))
+ {
+ /*
+ * The list is aligned with the one in
+ * nxagentHandleSelectionRequestFromXServer.
+ */
+ Atom targets[] = {XA_STRING,
+ clientUTF8_STRING,
+#ifdef SUPPORT_TEXT_TARGET
+ clientTEXT,
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
+ clientCOMPOUND_TEXT,
+#endif
+ clientTARGETS,
+ clientTIMESTAMP};
+ int numTargets = sizeof(targets) / sizeof(targets[0]);
- #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
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Sending %d available targets:\n", __func__, numTargets);
+ for (int i = 0; i < numTargets; i++)
+ {
+ fprintf(stderr, "%s: %d %s\n", __func__, targets[i], NameForLocalAtom(targets[i]));
+ }
+ #endif
- ChangeWindowProperty(pWin,
- property,
- MakeAtom("ATOM", 4, 1),
- sizeof(Atom)*8,
- PropModeReplace,
- numTargets,
- &targets,
- 1);
+ ChangeWindowProperty(pWin,
+ property,
+ MakeAtom("ATOM", 4, 1),
+ sizeof(Atom)*8,
+ PropModeReplace,
+ numTargets,
+ targets,
+ 1);
- sendSelectionNotifyEventToClient(client, time, requestor, selection, target, property);
+ sendSelectionNotifyEventToClient(client, time, requestor, selection,
+ target, property);
- return 1;
+ return 1;
+ }
+ else
+ {
+ /*
+ * Shortcut: Some applications tend to post multiple
+ * SelectionRequests. Further it can happen that multiple
+ * clients are interested in clipboard content. If we already
+ * know the answer and no intermediate SelectionOwner event
+ * occured we can answer with the cached list of targets.
+ */
+
+ if (targetCache[index].type == FOR_LOCAL && targetCache[index].forLocal)
+ {
+ Atom *targets = targetCache[index].forLocal;
+ int numTargets = targetCache[index].numTargets;
+
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Sending %d cached targets to local client:\n", __func__, numTargets);
+ for (int i = 0; i < numTargets; i++)
+ {
+ fprintf(stderr, "%s: %d %s\n", __func__, targets[i], NameForLocalAtom(targets[i]));
+ }
+ #endif
+
+ ChangeWindowProperty(pWin,
+ property,
+ MakeAtom("ATOM", 4, 1),
+ sizeof(Atom)*8,
+ PropModeReplace,
+ numTargets,
+ targets,
+ 1);
+
+ sendSelectionNotifyEventToClient(client, time, requestor, selection,
+ target, property);
+
+ return 1;
+ }
+
+ /*
+ * Do nothing - TARGETS will be handled like any other target
+ * and passed on to the owner on the remote side.
+ */
+ }
}
/*
@@ -1850,139 +2649,344 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
* support conversion to TIMESTAMP, returning the timestamp they
* used to obtain the selection."
*/
- if (target == clientTIMESTAMP)
- {
- int i = nxagentFindCurrentSelectionIndex(selection);
- if (i < NumCurrentSelections)
- {
- /*
- * "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,
- XA_INTEGER,
- 32,
- PropModeReplace,
- 1,
- (unsigned char *) &lastSelectionOwner[i].lastTimeChanged,
- 1);
-
- sendSelectionNotifyEventToClient(client, time, requestor, selection, target, property);
-
- return 1;
- }
- }
-
- if (lastClientClientPtr == client && (GetTimeInMillis() - lastClientReqTime < ACCUM_TIME))
+ else if (target == clientTIMESTAMP)
{
/*
- * The same client made consecutive requests of clipboard content
- * with less than 5 seconds time interval between them.
- * FIXME: this does not take the selection into account, so a
- * client requesting PRIMARY and CLIPBOARD would match here, too
+ * From ICCCM:
+ * "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,
+ XA_INTEGER,
+ 32,
+ PropModeReplace,
+ 1,
+ (unsigned char *) &lastSelectionOwner[index].lastTimeChanged,
+ 1);
+ sendSelectionNotifyEventToClient(client, time, requestor, selection, target, property);
+ return 1;
+ }
+ else if (target == clientMULTIPLE)
+ {
#ifdef DEBUG
- fprintf(stderr, "%s: Consecutives request from client %s selection [%u] "
- "elapsed time [%u] clientAccum [%d]\n", __func__, nxagentClientInfoString(client),
- selection, GetTimeInMillis() - lastClientReqTime, clientAccum);
+ fprintf(stderr, "%s: (currently) unsupported target [MULTIPLE] - denying request.\n", __func__);
#endif
-
- clientAccum++;
+ sendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
+ return 1;
}
- else
+ else if (target == clientDELETE)
{
- /* reset clientAccum as now another client requested the clipboard content */
- if (lastClientClientPtr != client)
- {
- clientAccum = 0;
- }
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (currently) unsupported target [DELETE] - denying request.\n", __func__);
+ #endif
+ sendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
+ return 1;
}
-
- if (target == clientTEXT ||
- target == XA_STRING ||
- target == clientCOMPOUND_TEXT ||
- target == clientUTF8_STRING)
+ else if (target == clientINSERT_SELECTION)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (currently) unsupported target [INSERT_SELECTION] - denying request.\n", __func__);
+ #endif
+ sendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
+ return 1;
+ }
+ else if (target == clientINSERT_PROPERTY)
{
- setClientSelectionStage(SelectionStageNone);
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (currently) unsupported target [INSERT_PROPERTY] - denying request.\n", __func__);
+ #endif
+ sendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
+ return 1;
+ }
+ else if (target == clientSAVE_TARGETS)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (currently) unsupported target [SAVE_TARGETS] - denying request.\n", __func__);
+ #endif
+ sendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
+ return 1;
+ }
+ else if (target == clientTARGET_SIZES)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (currently) unsupported target [TARGET_SIZES] - denying request.\n", __func__);
+ #endif
+ sendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
+ return 1;
+ }
- /*
- * store the original requestor, we need that later after
- * serverTransToAgentProperty contains the desired selection content
- */
- lastClientRequestor = requestor;
- lastClientWindowPtr = pWin;
- lastClientClientPtr = client;
- lastClientTime = time;
- lastClientProperty = property;
- lastClientSelection = selection;
- lastClientTarget = target;
-
- /* if the last client request time is more than 5s ago update it. Why? */
- if ((GetTimeInMillis() - lastClientReqTime) >= CONVERSION_TIMEOUT)
- lastClientReqTime = GetTimeInMillis();
-
- if (selection == clientCLIPBOARD)
+ /* In TextClipboard mode we reject all non-text targets. */
+ if (nxagentOption(TextClipboard))
+ {
+ if (!isTextTarget(translateLocalToRemoteTarget(target)))
{
- selection = lastSelectionOwner[nxagentClipboardSelection].selection;
+ #ifdef DEBUG
+ fprintf(stderr, "%s: denying request for non-text target [%d][%s].\n", __func__,
+ target, NameForLocalAtom(target));
+ #endif
+
+ sendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
+ return 1;
}
+ /* Go on, target is acceptable! */
+ }
+ #ifdef DEBUG
+ fprintf(stderr, "%s: target [%d][%s].\n", __func__, target,
+ NameForLocalAtom(target));
+ #endif
+
+ if (!nxagentOption(TextClipboard))
+ {
/*
- * we only convert to either UTF8 or XA_STRING, despite accepting
- * TEXT and COMPOUND_TEXT.
+ * Optimization: if we have a current target cache check if the
+ * requested target is supported by the owner. If not we can take
+ * a shortcut and deny the request immediately without doing any
+ * further communication
*/
- XlibAtom p = serverTransToAgentProperty;
- XlibAtom t;
- #ifdef DEBUG
- char * pstr = "NX_CUT_BUFFER_SERVER";
- const char * tstr;
- #endif
- if (target == clientUTF8_STRING)
+ if (targetCache[index].type == FOR_LOCAL && targetCache[index].forLocal)
{
- t = serverUTF8_STRING;
+ Atom *targets = targetCache[index].forLocal;
+
#ifdef DEBUG
- tstr = szAgentUTF8_STRING;
+ fprintf(stderr, "%s: Checking target validity\n", __func__);
#endif
+ Bool match = False;
+ for (int i = 0; i < targetCache[index].numTargets; i++)
+ {
+ if (targets[i] == target)
+ {
+ match = True;
+ break;
+ }
+ }
+ if (!match)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: target [%d][%s] is not supported by the owner - denying request.\n",
+ __func__, target, NameForLocalAtom(target));
+ #endif
+ sendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
+ return 1;
+ }
}
else
{
- t = XA_STRING;
+ /*
+ * At this stage we know a client has asked for a selection
+ * target without having retrieved the list of supported targets
+ * first.
+ */
#ifdef DEBUG
- tstr = validateString(NameForAtom(XA_STRING));
+ if (target != clientTARGETS)
+ {
+ fprintf(stderr, "%s: WARNING: client has not retrieved TARGETS before asking for selection!\n",
+ __func__);
+ }
#endif
}
+ }
+ if (lastClients[index].clientPtr == client)
+ {
#ifdef DEBUG
- fprintf(stderr, "%s: Sending XConvertSelection to real X server: requestor [0x%x] target [%ld][%s] property [%ld][%s] time [%ld]\n", __func__,
- serverWindow, t, tstr, p, pstr, CurrentTime);
+ fprintf(stderr, "%s: same client as previous request\n", __func__);
#endif
- XConvertSelection(nxagentDisplay, selection, t, p, serverWindow, CurrentTime);
+ if (GetTimeInMillis() - lastClients[index].reqTime < ACCUM_TIME)
+ {
+ /*
+ * 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 %s selection [%u] "
+ "elapsed time [%u] clientAccum [%d]\n", __func__,
+ nxagentClientInfoString(client),
+ selection, GetTimeInMillis() - lastClients[index].reqTime,
+ clientAccum);
+ #endif
+
+ clientAccum++;
+ }
+ }
+ else
+ {
+ /*
+ * Reset clientAccum as now another client requested the clipboard
+ * content.
+ */
+ clientAccum = 0;
+ }
- /* FIXME: check returncode of XConvertSelection */
+ setClientSelectionStage(index, SelectionStageNone);
- #ifdef DEBUG
- fprintf(stderr, "%s: Sent XConvertSelection with target [%s], property [%s]\n", __func__, tstr, pstr);
- #endif
+ /*
+ * Store the original requestor, we need that later after
+ * serverTransToAgentProperty has been filled with the desired
+ * selection content.
+ */
+ lastClients[index].requestor = requestor;
+ lastClients[index].windowPtr = pWin;
+ lastClients[index].clientPtr = client;
+ lastClients[index].time = time;
+ lastClients[index].property = property;
+ lastClients[index].target = target;
+ /* If the last client request time is more than 5s ago update it. Why? */
+ if ((GetTimeInMillis() - lastClients[index].reqTime) >= CONVERSION_TIMEOUT)
+ lastClients[index].reqTime = GetTimeInMillis();
+
+ XlibAtom remSelection = translateLocalToRemoteSelection(selection);
+ XlibAtom remTarget = translateLocalToRemoteTarget(target);
+ XlibAtom remProperty = serverTransToAgentProperty;
- return 1;
+ #ifdef DEBUG
+ fprintf(stderr, "%s: replacing local by remote property: [%d][%s] -> [%ld][%s]\n",
+ __func__, property, NameForLocalAtom(property),
+ remProperty, "NX_CUT_BUFFER_SERVER");
+ #endif
+
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Sending XConvertSelection to real X server: requestor [0x%lx] target [%ld][%s] property [%ld][%s] selection [%ld][%s] time [0][CurrentTime]\n", __func__,
+ serverWindow, remTarget, NameForRemoteAtom(remTarget),
+ remProperty, NameForRemoteAtom(remProperty),
+ remSelection, NameForRemoteAtom(remSelection));
+ #endif
+
+ /*
+ * ICCCM: "It is necessary for requestors to delete the property
+ * before issuing the request so that the target can later be
+ * extended to take parameters without introducing an
+ * incompatibility. Also note that the requestor of a selection need
+ * not know the client that owns the selection nor the window on
+ * which the selection was acquired."
+ */
+
+ XDeleteProperty(nxagentDisplay, serverWindow, remProperty);
+
+ /*
+ * FIXME: ICCCM states: "Clients should not use CurrentTime for the
+ * time argument of a ConvertSelection request. Instead, they should
+ * use the timestamp of the event that caused the request to be
+ * made." Well, the event that that caused this came from an
+ * nxagent _client_ but we are a client to the real X server, which
+ * has an own time., we cannot use its time there. So what time
+ * would be correct here?
+ */
+
+ UpdateCurrentTime();
+
+ XConvertSelection(nxagentDisplay, remSelection, remTarget, remProperty,
+ serverWindow, CurrentTime);
+
+ NXFlushDisplay(nxagentDisplay, NXFlushLink);
+
+ /* XConvertSelection will always return 1 (check the source!), so no
+ need to check the return code. */
+
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Sent XConvertSelection\n", __func__);
+ #endif
+
+ return 1;
+}
+
+/*
+ * FIXME: do we still need this special treatment? Can't we just
+ * call nxagentLocalToRemoteAtom() everywhere?
+ */
+XlibAtom translateLocalToRemoteSelection(Atom local)
+{
+ /*
+ * On the real server, the right CLIPBOARD atom is
+ * XInternAtom(nxagentDisplay, "CLIPBOARD", 1), which is stored in
+ * remoteSelectionAtoms[nxagentClipboardSelection]. For
+ * PRIMARY there's nothing to map because that is identical on all
+ * X servers (defined in Xatom.h). We do it anyway so we do not
+ * require a special treatment.
+ */
+
+ XlibAtom remote = -1;
+
+ for (int index = 0; index < nxagentMaxSelections; index++)
+ {
+ if (local == localSelelectionAtoms[index])
+ {
+ remote = remoteSelectionAtoms[index];
+ break;
+ }
}
- else
+
+ if (remote == -1)
{
- /* deny request */
- #ifdef DEBUG
- fprintf(stderr, "%s: Unsupported target [%d][%s] - denying request\n", __func__, target,
- validateString(NameForAtom(target)));
- #endif
- sendSelectionNotifyEventToClient(client, time, requestor, selection, target, None);
+ remote = nxagentLocalToRemoteAtom(local);
+ }
- return 1;
+ #ifdef DEBUG
+ fprintf(stderr, "%s: mapping local to remote selection: [%d][%s] -> [%ld][%s]\n", __func__,
+ local, NameForLocalAtom(local), remote, NameForRemoteAtom(remote));
+ #endif
+
+ return remote;
+}
+
+/* FIXME: do we still need this special treatment? Can't we just
+ call nxagentLocalToRemoteAtom() everywhere? */
+XlibAtom translateLocalToRemoteTarget(Atom local)
+{
+ /*
+ * .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.
+ */
+
+ XlibAtom remote;
+
+ /*
+ * we only convert to either UTF8 or XA_STRING
+#ifdef SUPPORT_TEXT_TARGET
+ * despite accepting TEXT
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
+ * and COMPOUND_TEXT.
+#endif
+ */
+
+ if (local == clientUTF8_STRING)
+ {
+ remote = serverUTF8_STRING;
+ }
+#ifdef SUPPORT_TEXT_TARGET
+ else if (local == clientTEXT)
+ {
+ remote = serverTEXT;
+ }
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
+ else if (local == clientCOMPOUND_TEXT)
+ {
+ remote = serverCOMPOUND_TEXT;
}
- return 0;
+#endif
+ else if (local == clientTARGETS)
+ {
+ remote = serverTARGETS;
+ }
+ else
+ {
+ remote = nxagentLocalToRemoteAtom(local);
+ }
+
+ #ifdef DEBUG
+ fprintf(stderr, "%s: mapping local to remote target: [%d][%s] -> [%ld][%s]\n", __func__,
+ local, NameForLocalAtom(local), remote, NameForRemoteAtom(remote));
+ #endif
+
+ return remote;
}
/*
@@ -2003,26 +3007,36 @@ int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
* selection. When there is an owner, it should be generated by the
* owner of the selection by using XSendEvent()."
*/
-int nxagentSendNotify(xEvent *event)
+int nxagentSendNotificationToSelfViaXServer(xEvent *event)
{
+ if (!agentClipboardInitialized)
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: clipboard not initialized - doing nothing.\n", __func__);
+ #endif
+ return 0;
+ }
+
#ifdef DEBUG
- fprintf(stderr, "%s: Got called.\n", __func__);
+ fprintf(stderr, "---------\n%s: Received SendNotify from client: property [%d][%s] target [%d][%s] selection [%d][%s] requestor [0x%x] time [%u].\n", __func__,
+ event->u.selectionNotify.property, NameForLocalAtom(event->u.selectionNotify.property),
+ event->u.selectionNotify.target, NameForLocalAtom(event->u.selectionNotify.target),
+ event->u.selectionNotify.selection, NameForLocalAtom(event->u.selectionNotify.selection),
+ event->u.selectionNotify.requestor, event->u.selectionNotify.time);
#endif
- if (!agentClipboardInitialized)
+ int index = nxagentFindCurrentSelectionIndex(event->u.selectionNotify.selection);
+ if (index == -1)
{
#ifdef DEBUG
- fprintf(stderr, "%s: clipboard not initialized - doing nothing.\n", __func__);
+ fprintf(stderr, "%s: unknown selection [%d]\n", __func__,
+ event->u.selectionNotify.selection);
#endif
return 0;
}
#ifdef DEBUG
- 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);
+ fprintf(stderr, "%s: lastServers[index].requestor is [0x%lx].\n", __func__, lastServers[index].requestor);
#endif
/*
@@ -2031,9 +3045,9 @@ int nxagentSendNotify(xEvent *event)
* 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!
- * Be sure to not let this trigger for the failure answer (property 0)
+ * Be sure to not let this trigger for the failure answer (property 0).
*/
- if (!(event->u.selectionNotify.property == clientCutProperty || event->u.selectionNotify.property == 0) || lastServerRequestor == None)
+ if (!(event->u.selectionNotify.property == clientCutProperty || event->u.selectionNotify.property == 0) || lastServers[index].requestor == None)
{
#ifdef DEBUG
fprintf(stderr, "%s: sent nothing - message to real X server is not required.\n", __func__);
@@ -2043,98 +3057,70 @@ int nxagentSendNotify(xEvent *event)
else
{
/*
- * 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 few other
- * properties except serverTransToAgentProperty, the only thing
- * we need to ensure is that the internal Atom clientCutProperty
- * differs from the server-side serverTransToAgentProperty
- * Atom. The actual name is not important. To be clean here we use
- * a separate serverTransFromAgentProperty.
- */
-
- XSelectionEvent eventSelection = {
- .requestor = serverWindow,
- .selection = event->u.selectionNotify.selection,
- .target = event->u.selectionNotify.target,
- .property = serverTransFromAgentProperty,
- .time = CurrentTime,
- };
-
- /*
- * 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 == clientCLIPBOARD)
- {
- 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 the property is 0 (reporting failure) we can directly
+ * answer to the lastServer about the failure!
*/
-
- 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)
+ if (lastServers[index].requestor != None && event->u.selectionNotify.property == 0)
{
- eventSelection.target = serverCOMPOUND_TEXT;
+ replyPendingRequestSelectionToXServer(index, False);
+ return 1;
}
else
{
- eventSelection.target = XA_STRING;
- }
-
- #ifdef DEBUG
- 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
+ /*
+ * 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 few other
+ * properties except serverTransToAgentProperty, the only thing
+ * we need to ensure is that the local Atom clientCutProperty
+ * differs from the server-side serverTransToAgentProperty
+ * Atom. The actual name is not important. To be clean here we use
+ * a separate serverTransFromAgentProperty.
+ */
- sendSelectionNotifyEventToServer(&eventSelection);
+ XSelectionEvent eventSelection = {
+ .requestor = serverWindow,
+ .selection = translateLocalToRemoteSelection(event->u.selectionNotify.selection),
+ .target = translateLocalToRemoteTarget(event->u.selectionNotify.target),
+ .property = serverTransFromAgentProperty,
+ .time = CurrentTime,
+ };
- return 1;
+ #ifdef DEBUG
+ fprintf(stderr, "%s: remote property [%ld][%s].\n", __func__,
+ serverTransFromAgentProperty, NameForRemoteAtom(serverTransFromAgentProperty));
+ #endif
+ sendSelectionNotifyEventToXServer(&eventSelection);
+ return 1;
+ }
}
}
/*
* This is called from NXproperty.c to determine if a client sets the
* property we are waiting for.
- * FIXME: in addition we should check if the client is the one we expect
+ * FIXME: in addition we should check if the client is the one we expect.
*/
WindowPtr nxagentGetClipboardWindow(Atom property)
{
- int i = nxagentFindLastSelectionOwnerIndex(serverLastRequestedSelection);
- if (i < nxagentMaxSelections &&
+ if (serverLastRequestedSelection == -1)
+ {
+ return NULL;
+ }
+
+ int index = nxagentFindRemoteSelectionIndex(serverLastRequestedSelection);
+ if (index != -1 &&
property == clientCutProperty &&
- lastSelectionOwner[i].windowPtr != NULL)
+ lastSelectionOwner[index].windowPtr != NULL)
{
#ifdef DEBUG
- fprintf(stderr, "%s: Returning last [%ld] selection owner window [%p] (0x%x).\n", __func__,
- lastSelectionOwner[i].selection,
- (void *)lastSelectionOwner[i].windowPtr, WINDOWID(lastSelectionOwner[i].windowPtr));
+ fprintf(stderr, "%s: Returning last [%d] selection owner window [%p] (0x%x).\n", __func__,
+ localSelelectionAtoms[index],
+ (void *)lastSelectionOwner[index].windowPtr, WINDOWID(lastSelectionOwner[index].windowPtr));
#endif
- return lastSelectionOwner[i].windowPtr;
+ return lastSelectionOwner[index].windowPtr;
}
else
{
@@ -2143,7 +3129,7 @@ WindowPtr nxagentGetClipboardWindow(Atom property)
}
/*
- * Initialize the clipboard
+ * Initialize the clipboard.
* Returns: True for success else False
*/
Bool nxagentInitClipboard(WindowPtr pWin)
@@ -2154,7 +3140,7 @@ Bool nxagentInitClipboard(WindowPtr pWin)
#ifdef NXAGENT_TIMESTAMP
{
- fprintf(stderr, "%s: Clipboard init starts at [%ld] ms.\n", __func__,
+ fprintf(stderr, "%s: Clipboard init starts at [%lu] ms.\n", __func__,
GetTimeInMillis() - startTime);
}
#endif
@@ -2164,33 +3150,101 @@ Bool nxagentInitClipboard(WindowPtr pWin)
if (!nxagentReconnectTrap)
{
- SAFE_free(lastSelectionOwner);
+ /* Cannot move that down to others - we need it for
+ * initSelectionOwnerData! */
+ /* FIXME: it is probably better to re-use the strings from Atoms.c here */
+ clientTARGETS = MakeAtom(szAgentTARGETS, strlen(szAgentTARGETS), True);
+#ifdef SUPPORT_TEXT_TARGET
+ clientTEXT = MakeAtom(szAgentTEXT, strlen(szAgentTEXT), True);
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
+ clientCOMPOUND_TEXT = MakeAtom(szAgentCOMPOUND_TEXT, strlen(szAgentCOMPOUND_TEXT), True);
+#endif
+ clientUTF8_STRING = MakeAtom(szAgentUTF8_STRING, strlen(szAgentUTF8_STRING), True);
+ clientTIMESTAMP = MakeAtom(szAgentTIMESTAMP, strlen(szAgentTIMESTAMP), True);
+ clientINCR = MakeAtom(szAgentINCR, strlen(szAgentINCR), True);
+ clientMULTIPLE = MakeAtom(szAgentMULTIPLE, strlen(szAgentMULTIPLE), True);
+ clientDELETE = MakeAtom(szAgentDELETE, strlen(szAgentDELETE), True);
+ clientINSERT_SELECTION = MakeAtom(szAgentINSERT_SELECTION, strlen(szAgentINSERT_SELECTION), True);
+ clientINSERT_PROPERTY = MakeAtom(szAgentINSERT_PROPERTY, strlen(szAgentINSERT_PROPERTY), True);
+ clientSAVE_TARGETS = MakeAtom(szAgentSAVE_TARGETS, strlen(szAgentSAVE_TARGETS), True);
+ clientTARGET_SIZES = MakeAtom(szAgentTARGET_SIZES, strlen(szAgentTARGET_SIZES), True);
+ SAFE_free(lastSelectionOwner);
lastSelectionOwner = (SelectionOwner *) malloc(nxagentMaxSelections * sizeof(SelectionOwner));
if (lastSelectionOwner == NULL)
{
FatalError("nxagentInitClipboard: Failed to allocate memory for the clipboard selections.\n");
}
- initSelectionOwner(nxagentPrimarySelection, XA_PRIMARY);
- initSelectionOwner(nxagentClipboardSelection, nxagentAtoms[10]); /* CLIPBOARD */
- }
- else
- {
- /* the clipboard selection atom might have changed on a new X
- server. Primary is constant. */
- lastSelectionOwner[nxagentClipboardSelection].selection = nxagentAtoms[10]; /* CLIPBOARD */
+ initSelectionOwnerData(nxagentPrimarySelection);
+ initSelectionOwnerData(nxagentClipboardSelection);
+
+ SAFE_free(lastClients);
+ lastClients = (lastClient *) calloc(nxagentMaxSelections, sizeof(lastClient));
+ if (lastClients == NULL)
+ {
+ FatalError("nxagentInitClipboard: Failed to allocate memory for the last clients array.\n");
+ }
+
+ SAFE_free(lastServers);
+ lastServers = (lastServer *) calloc(nxagentMaxSelections, sizeof(lastServer));
+ if (lastServers == NULL)
+ {
+ FatalError("nxagentInitClipboard: Failed to allocate memory for the last servers array.\n");
+ }
+
+ SAFE_free(localSelelectionAtoms);
+ localSelelectionAtoms = (Atom *) calloc(nxagentMaxSelections, sizeof(Atom));
+ if (localSelelectionAtoms == NULL)
+ {
+ FatalError("nxagentInitClipboard: Failed to allocate memory for the local selection Atoms array.\n");
+ }
+ localSelelectionAtoms[nxagentPrimarySelection] = XA_PRIMARY;
+ localSelelectionAtoms[nxagentClipboardSelection] = MakeAtom(szAgentCLIPBOARD, strlen(szAgentCLIPBOARD), True);
+
+ SAFE_free(remoteSelectionAtoms);
+ remoteSelectionAtoms = (XlibAtom *) calloc(nxagentMaxSelections, sizeof(XlibAtom));
+ if (remoteSelectionAtoms == NULL)
+ {
+ FatalError("nxagentInitClipboard: Failed to allocate memory for the remote selection Atoms array.\n");
+ }
+
+ SAFE_free(targetCache);
+ targetCache = (Targets *) calloc(nxagentMaxSelections, sizeof(Targets));
+ if (targetCache == NULL)
+ {
+ FatalError("nxagentInitClipboard: Failed to allocate memory for target cache.\n");
+ }
}
+ /*
+ * The clipboard selection atom can change with a new X
+ * server while Primary is constant.
+ */
+ remoteSelectionAtoms[nxagentPrimarySelection] = XA_PRIMARY;
+ remoteSelectionAtoms[nxagentClipboardSelection] = nxagentAtoms[10]; /* CLIPBOARD */
+
serverTARGETS = nxagentAtoms[6]; /* TARGETS */
+#ifdef SUPPORT_TEXT_TARGET
serverTEXT = nxagentAtoms[7]; /* TEXT */
+#endif
+#ifdef SUPPORT_COMPOUND_TEXT_TARGET
serverCOMPOUND_TEXT = nxagentAtoms[16]; /* COMPOUND_TEXT */
+#endif
serverUTF8_STRING = nxagentAtoms[12]; /* UTF8_STRING */
serverTIMESTAMP = nxagentAtoms[11]; /* TIMESTAMP */
+ serverINCR = nxagentAtoms[17]; /* INCR */
+ serverMULTIPLE = nxagentAtoms[18]; /* MULTIPLE */
+ serverDELETE = nxagentAtoms[19]; /* DELETE */
+ serverINSERT_SELECTION = nxagentAtoms[20]; /* INSERT_SELECTION */
+ serverINSERT_PROPERTY = nxagentAtoms[21]; /* INSERT_PROPERTY */
+ serverSAVE_TARGETS = nxagentAtoms[22]; /* SAVE_TARGETS */
+ serverTARGET_SIZES = nxagentAtoms[23]; /* TARGET_SIZES */
/*
* Server side properties to hold pasted data.
- * see nxagentSendNotify for an explanation
+ * See nxagentSendNotificationToSelfViaXServer for an explanation
*/
serverTransFromAgentProperty = nxagentAtoms[15]; /* NX_SELTRANS_FROM_AGENT */
serverTransToAgentProperty = nxagentAtoms[5]; /* NX_CUT_BUFFER_SERVER */
@@ -2204,12 +3258,15 @@ Bool nxagentInitClipboard(WindowPtr pWin)
return False;
}
+#ifdef NXAGENT_ONSTART
+ /* This is probably to communicate with nomachine nxclient. */
#ifdef TEST
- fprintf(stderr, "%s: Setting owner of selection [%d][%s] on window 0x%x\n", __func__,
+ fprintf(stderr, "%s: Setting owner of selection [%d][%s] to serverWindow [0x%lx]\n", __func__,
(int) serverTransToAgentProperty, "NX_CUT_BUFFER_SERVER", serverWindow);
#endif
XSetSelectionOwner(nxagentDisplay, serverTransToAgentProperty, serverWindow, CurrentTime);
+#endif
if (XQueryExtension(nxagentDisplay,
"XFIXES",
@@ -2225,10 +3282,10 @@ Bool nxagentInitClipboard(WindowPtr pWin)
fprintf(stderr, "%s: Registering for XFixesSelectionNotify events.\n", __func__);
#endif
- for (int i = 0; i < nxagentMaxSelections; i++)
+ for (int index = 0; index < nxagentMaxSelections; index++)
{
XFixesSelectSelectionInput(nxagentDisplay, serverWindow,
- lastSelectionOwner[i].selection,
+ remoteSelectionAtoms[index],
XFixesSetSelectionOwnerNotifyMask |
XFixesSelectionWindowDestroyNotifyMask |
XFixesSelectionClientCloseNotifyMask);
@@ -2248,14 +3305,13 @@ Bool nxagentInitClipboard(WindowPtr pWin)
if (nxagentSessionId[0])
{
- // nxagentAtoms[10] is the CLIPBOARD atom
#ifdef TEST
fprintf(stderr, "%s: setting the ownership of %s to %lx"
" and registering for PropertyChangeMask events\n", __func__,
- validateString(XGetAtomName(nxagentDisplay, nxagentAtoms[10])), serverWindow);
+ validateString(NameForRemoteAtom(nxagentAtoms[10])), serverWindow);
#endif
- XSetSelectionOwner(nxagentDisplay, nxagentAtoms[10], serverWindow, CurrentTime);
+ XSetSelectionOwner(nxagentDisplay, nxagentAtoms[10], serverWindow, CurrentTime); // nxagentAtoms[10] is the CLIPBOARD atom
pWin -> eventMask |= PropertyChangeMask;
nxagentChangeWindowAttributes(pWin, CWEventMask);
}
@@ -2267,40 +3323,47 @@ Bool nxagentInitClipboard(WindowPtr pWin)
if (nxagentOption(Clipboard) == ClipboardServer ||
nxagentOption(Clipboard) == ClipboardBoth)
{
- for (int i = 0; i < nxagentMaxSelections; i++)
+ for (int index = 0; index < nxagentMaxSelections; index++)
{
/*
- * if we have a selection inform the (new) real Xserver and
+ * If we have a selection inform the (new) real Xserver and
* claim the ownership. Note that we report our serverWindow as
* owner, not the real window!
*/
- if (IS_INTERNAL_OWNER(i) && lastSelectionOwner[i].window)
- {
- XSetSelectionOwner(nxagentDisplay, lastSelectionOwner[i].selection, serverWindow, CurrentTime);
- }
+ if (IS_LOCAL_OWNER(index) && lastSelectionOwner[index].window)
+ {
+ /* remoteSelectionAtoms have already been adjusted above */
+ XSetSelectionOwner(nxagentDisplay, remoteSelectionAtoms[index], serverWindow, CurrentTime);
+ }
+
+ /*
+ * On reconnect there cannot be any external requestor
+ * waiting for a reply so clean this.
+ */
+ lastServers[index].requestor = None;
+
+ /*
+ * FIXME: We should reset lastClients[index].* here! Problem
+ * is that local clients might still be waiting for
+ * answers. Should reply with failure then.
+ */
+
+ invalidateTargetCache(index);
}
}
- /* FIXME: Shouldn't we reset lastServer* and lastClient* here? */
}
else
{
- for (int i = 0; i < nxagentMaxSelections; i++)
+ for (int index = 0; index < nxagentMaxSelections; index++)
{
- clearSelectionOwner(i);
+ clearSelectionOwnerData(index);
+ resetClientSelectionStage(index);
+ /* FIXME: required? move to setSelectionStage? */
+ lastClients[index].reqTime = GetTimeInMillis();
+ lastServers[index].requestor = None;
+ invalidateTargetCache(index);
}
- lastServerRequestor = None;
-
- setClientSelectionStage(SelectionStageNone);
- lastClientReqTime = GetTimeInMillis();
-
- clientTARGETS = MakeAtom(szAgentTARGETS, strlen(szAgentTARGETS), True);
- clientTEXT = MakeAtom(szAgentTEXT, strlen(szAgentTEXT), True);
- clientCOMPOUND_TEXT = MakeAtom(szAgentCOMPOUND_TEXT, strlen(szAgentCOMPOUND_TEXT), True);
- clientUTF8_STRING = MakeAtom(szAgentUTF8_STRING, strlen(szAgentUTF8_STRING), True);
- clientTIMESTAMP = MakeAtom(szAgentTIMESTAMP, strlen(szAgentTIMESTAMP), True);
- clientCLIPBOARD = MakeAtom(szAgentCLIPBOARD, strlen(szAgentCLIPBOARD), True);
-
clientCutProperty = MakeAtom(szAgentNX_CUT_BUFFER_CLIENT,
strlen(szAgentNX_CUT_BUFFER_CLIENT), True);
if (clientCutProperty == None)
@@ -2322,7 +3385,7 @@ Bool nxagentInitClipboard(WindowPtr pWin)
#ifdef NXAGENT_TIMESTAMP
{
- fprintf(stderr, "%s: Clipboard init ends at [%ld] ms.\n", __func__,
+ fprintf(stderr, "%s: Clipboard init ends at [%lu] ms.\n", __func__,
GetTimeInMillis() - startTime);
}
#endif
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Clipboard.h b/nx-X11/programs/Xserver/hw/nxagent/Clipboard.h
index d401dae51..4518595d1 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Clipboard.h
+++ b/nx-X11/programs/Xserver/hw/nxagent/Clipboard.h
@@ -57,11 +57,14 @@ extern void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow);
extern int nxagentConvertSelection(ClientPtr client, WindowPtr pWin, Atom selection,
Window requestor, Atom property, Atom target, Time time);
+extern void nxagentHandleSelectionClearFromXServerByIndex(int index);
#ifdef XEvent
+extern void nxagentHandleSelectionClearFromXServerByAtom(XlibAtom sel);
extern void nxagentHandleSelectionClearFromXServer(XEvent *X);
extern void nxagentHandleSelectionRequestFromXServer(XEvent *X);
extern void nxagentHandleSelectionNotifyFromXServer(XEvent *X);
#else
+extern void nxagentHandleSelectionClearFromXServerByAtom();
extern void nxagentHandleSelectionClearFromXServer();
extern void nxagentHandleSelectionRequestFromXServer();
extern void nxagentHandleSelectionNotifyFromXServer();
@@ -72,11 +75,11 @@ extern int nxagentFindCurrentSelectionIndex(Atom sel);
* Handle the selection property received in the event loop in
* Events.c.
*/
-extern void nxagentCollectPropertyEvent(int resource);
+extern Bool nxagentCollectPropertyEventFromXServer(int resource);
extern WindowPtr nxagentGetClipboardWindow(Atom property);
-extern int nxagentSendNotify(xEvent *event);
+extern int nxagentSendNotificationToSelfViaXServer(xEvent *event);
extern void nxagentDumpClipboardStat(void);
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Events.c b/nx-X11/programs/Xserver/hw/nxagent/Events.c
index 2a3654731..0340e0b57 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Events.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Events.c
@@ -131,8 +131,6 @@
extern Bool nxagentOnce;
-extern int nxagentLastClipboardClient;
-
#ifdef NX_DEBUG_INPUT
int nxagentDebugInput = 0;
#endif
@@ -2843,69 +2841,69 @@ int nxagentHandleXFixesSelectionNotify(XEvent *X)
return 0;
}
+ #ifdef DEBUG
+ fprintf(stderr, "---------\n");
+ #endif
+
#ifdef TEST
fprintf(stderr, "%s: Handling event.\n", __func__);
#endif
- if (SelectionCallback)
- {
- Atom local = nxagentRemoteToLocalAtom(xfixesEvent -> xfixesselection.selection);
-
- int i = nxagentFindCurrentSelectionIndex(local);
- if (i < NumCurrentSelections)
- {
- if (CurrentSelections[i].client != 0)
- {
- #ifdef TEST
- fprintf(stderr, "%s: Do nothing.\n", __func__);
- #endif
-
- return 1;
- }
-
- #ifdef TEST
- fprintf(stderr, "%s: Calling callbacks for %d [%s] selection.\n", __func__,
- CurrentSelections[i].selection, NameForAtom(CurrentSelections[i].selection));
- #endif
+ #ifdef DEBUG
+ fprintf(stderr, "%s: Event timestamp [%ld]\n", __func__, xfixesEvent->xfixesselection.timestamp);
+ fprintf(stderr, "%s: Event selection timestamp [%ld]\n", __func__, xfixesEvent->xfixesselection.selection_timestamp);
+ fprintf(stderr, "%s: Event selection window [0x%lx]\n", __func__, xfixesEvent->xfixesselection.window);
+ fprintf(stderr, "%s: Event selection owner [0x%lx]\n", __func__, xfixesEvent->xfixesselection.owner);
+ fprintf(stderr, "%s: Event selection [%s]\n", __func__, NameForAtom(nxagentRemoteToLocalAtom(xfixesEvent->xfixesselection.selection)));
- #ifdef DEBUG
- fprintf(stderr, "%s: CurrentSelections[%d].lastTimeChanged [%u]\n", __func__, i, CurrentSelections[i].lastTimeChanged.milliseconds);
- fprintf(stderr, "%s: Event timestamp [%ld]\n", __func__, xfixesEvent->xfixesselection.timestamp);
- fprintf(stderr, "%s: Event selection timestamp [%ld]\n", __func__, xfixesEvent->xfixesselection.selection_timestamp);
- fprintf(stderr, "%s: Event selection window [0x%lx]\n", __func__, xfixesEvent->xfixesselection.window);
- fprintf(stderr, "%s: Event selection owner [0x%lx]\n", __func__, xfixesEvent->xfixesselection.owner);
- fprintf(stderr, "%s: Event selection [%s]\n", __func__, NameForAtom(nxagentRemoteToLocalAtom(xfixesEvent->xfixesselection.selection)));
+ fprintf(stderr, "%s: Subtype ", __func__);
- fprintf(stderr, "%s: Subtype ", __func__);
+ switch (xfixesEvent -> xfixesselection.subtype)
+ {
+ case SelectionSetOwner: fprintf(stderr, "SelectionSetOwner.\n"); break;
+ case SelectionWindowDestroy: fprintf(stderr, "SelectionWindowDestroy.\n"); break;
+ case SelectionClientClose: fprintf(stderr, "SelectionClientClose.\n"); break;
+ default: fprintf(stderr, ".\n"); break;
+ }
+ #endif
- switch (xfixesEvent -> xfixesselection.subtype)
- {
- case SelectionSetOwner: fprintf(stderr, "SelectionSetOwner.\n"); break;
- case SelectionWindowDestroy: fprintf(stderr, "SelectionWindowDestroy.\n"); break;
- case SelectionClientClose: fprintf(stderr, "SelectionClientClose.\n"); break;
- default: fprintf(stderr, ".\n"); break;
- }
- #endif
+ if (xfixesEvent->xfixesselection.owner && xfixesEvent->xfixesselection.owner == nxagentWindow(screenInfo.screens[0]->root))
+ {
+ /*
+ * This is an event that must have been triggered by nxagent itself
+ * - by calling XSetSelectionOwner(). As this is no news for us we
+ * can ignore the event.
+ */
- SelectionInfoRec info = {
- .selection = &CurrentSelections[i],
- .kind = xfixesEvent->xfixesselection.subtype
- };
+ #ifdef DEBUG
+ fprintf(stderr, "%s: (new) owner is nxagent (window is [0x%lx]) - ignoring it.\n", __func__, xfixesEvent->xfixesselection.window);
+ #endif
+ return 0;
+ }
- /*
- * 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 would end up in an
- * infinite loop of events. If there was a better way to
- * identify that situation during Callback processing we could
- * get rid of the Trap...
- */
- nxagentExternalClipboardEventTrap = True;
- CallCallbacks(&SelectionCallback, &info);
- nxagentExternalClipboardEventTrap = False;
- }
+ /*
+ * Realistically the only situation where we can receive
+ * WindowDestroy or ClientClose will also end nxagent, so we do not
+ * need to handle them. But the code is here, so let's keep it.
+ */
+ if (xfixesEvent -> xfixesselection.subtype == SelectionSetOwner||
+ xfixesEvent -> xfixesselection.subtype == SelectionWindowDestroy ||
+ xfixesEvent -> xfixesselection.subtype == SelectionClientClose)
+ {
+ /*
+ * Reception of an owner change on the real X server is - for nxagent - the same as
+ * receiving a SelectionClear event. We just need to tell a (possible) internal
+ * owner that is no longer owning the selection.
+ */
+ nxagentHandleSelectionClearFromXServerByAtom(xfixesEvent -> xfixesselection.selection);
+ }
+ else
+ {
+ #ifdef DEBUG
+ fprintf(stderr, "%s: WARNING unexpected xfixesselection subtype [%d]\n", __func__, xfixesEvent -> xfixesselection.subtype);
+ #endif
}
+
return 1;
}
@@ -3984,11 +3982,7 @@ void nxagentHandleCollectPropertyEvent(XEvent *X)
return;
}
- if (resource == nxagentLastClipboardClient)
- {
- nxagentCollectPropertyEvent(resource);
- }
- else
+ if (!nxagentCollectPropertyEventFromXServer(resource))
{
XlibAtom atomReturnType;
int resultFormat;
@@ -4559,7 +4553,10 @@ void ForwardClientMessage(ClientPtr client, xSendEventReq *stuff)
XlibWindow dest;
dest = DefaultRootWindow(nxagentDisplay);
- Status stat = XSendEvent(nxagentDisplay, dest, stuff->propagate, stuff->eventMask, &X);
+ #ifdef DEBUG
+ Status stat =
+ #endif
+ XSendEvent(nxagentDisplay, dest, stuff->propagate, stuff->eventMask, &X);
XFlush(nxagentDisplay);
#ifdef DEBUG
fprintf(stderr, "%s: send to window [0x%lx]\n", __func__, dest);
diff --git a/nx-X11/programs/Xserver/hw/nxagent/NXdispatch.c b/nx-X11/programs/Xserver/hw/nxagent/NXdispatch.c
index a8739fa3f..5df008bbb 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/NXdispatch.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/NXdispatch.c
@@ -722,8 +722,8 @@ ProcConvertSelection(register ClientPtr client)
(stuff->selection == MakeAtom("CLIPBOARD", 9, 0))) &&
nxagentOption(Clipboard) != ClipboardNone)
{
- int i = nxagentFindCurrentSelectionIndex(stuff->selection);
- if ((i < NumCurrentSelections) && (CurrentSelections[i].window != None))
+ int index = nxagentFindCurrentSelectionIndex(stuff->selection);
+ if ((index != -1) && (CurrentSelections[index].window != None))
{
if (nxagentConvertSelection(client, pWin, stuff->selection, stuff->requestor,
stuff->property, stuff->target, stuff->time))
@@ -745,10 +745,15 @@ ProcConvertSelection(register ClientPtr client)
while ((i < NumCurrentSelections) &&
CurrentSelections[i].selection != stuff->selection) i++;
if ((i < NumCurrentSelections) &&
-#ifdef NXAGENT_SERVER
- (CurrentSelections[i].window != None) && (CurrentSelections[i].client != NullClient)
-#else
(CurrentSelections[i].window != None)
+#ifdef NXAGENT_SERVER
+ /*
+ * .window can be set and pointing to our server window to
+ * signal the clipboard owner being on the real X
+ * server. Therefore we need to check .client in addition
+ * to ensure having a local owner.
+ */
+ && (CurrentSelections[i].client != NullClient)
#endif
#ifdef XCSECURITY
&& (!client->CheckAccess ||
diff --git a/nx-X11/programs/Xserver/hw/nxagent/NXevents.c b/nx-X11/programs/Xserver/hw/nxagent/NXevents.c
index fccc718b0..0249fc0a8 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/NXevents.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/NXevents.c
@@ -433,8 +433,11 @@ ProcSendEvent(ClientPtr client)
if (stuff -> event.u.u.type == SelectionNotify)
{
- if (nxagentSendNotify(&stuff->event) == 1)
- return Success;
+ #ifdef DEBUG
+ fprintf(stderr, "%s: sending SelectionNotify to ourselves"\n, __func__);
+ #endif
+ if (nxagentSendNotificationToSelfViaXServer(&stuff->event) == 1)
+ return Success;
}
#endif
return xorg_ProcSendEvent(client);
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Options.c b/nx-X11/programs/Xserver/hw/nxagent/Options.c
index be6e5d5e1..8e6249e4c 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Options.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Options.c
@@ -88,6 +88,7 @@ void nxagentInitOptions(void)
nxagentOptions.BackingStore = BackingStoreUndefined;
nxagentOptions.Clipboard = ClipboardBoth;
+ nxagentOptions.TextClipboard = False;
nxagentOptions.SharedMemory = True;
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Options.h b/nx-X11/programs/Xserver/hw/nxagent/Options.h
index a99f4aee2..d3c8c7150 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Options.h
+++ b/nx-X11/programs/Xserver/hw/nxagent/Options.h
@@ -195,6 +195,13 @@ typedef struct _AgentOptions
ClipboardMode Clipboard;
/*
+ * transfer TARGETS to remote side or answer with a limited
+ * hardcoded text target list
+ * Should be Bool but we'd have to include Xlib.h for that
+ */
+ int TextClipboard;
+
+ /*
* Enable agent to use the MITSHM extension in path from remote
* proxy to the real X server.
*/
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Trap.c b/nx-X11/programs/Xserver/hw/nxagent/Trap.c
index 6eade2073..c0a1fdde0 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Trap.c
+++ b/nx-X11/programs/Xserver/hw/nxagent/Trap.c
@@ -103,11 +103,3 @@ Bool nxagentXkbCapsTrap = False;
*/
Bool nxagentXkbNumTrap = False;
-
-/*
- * Set to indicate we are processing a clipboard event triggered by
- * the real X server. This is used to avoid endless loops if callbacks
- * would trigger another event by the real X server
- */
-
-Bool nxagentExternalClipboardEventTrap = False;
diff --git a/nx-X11/programs/Xserver/hw/nxagent/Trap.h b/nx-X11/programs/Xserver/hw/nxagent/Trap.h
index 1c10f00ea..02ad48f99 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/Trap.h
+++ b/nx-X11/programs/Xserver/hw/nxagent/Trap.h
@@ -101,11 +101,4 @@ extern Bool nxagentXkbCapsTrap;
*/
extern Bool nxagentXkbNumTrap;
-/*
- * Set to indicate we are processing a clipboard event triggered by
- * the real X server. This is used to avoid endless loops if callbacks
- * would trigger another event by the real X server
- */
-extern Bool nxagentExternalClipboardEventTrap;
-
#endif /* __Trap_H__ */
diff --git a/nx-X11/programs/Xserver/hw/nxagent/man/nxagent.1 b/nx-X11/programs/Xserver/hw/nxagent/man/nxagent.1
index 74349cda7..f373cb239 100644
--- a/nx-X11/programs/Xserver/hw/nxagent/man/nxagent.1
+++ b/nx-X11/programs/Xserver/hw/nxagent/man/nxagent.1
@@ -465,6 +465,10 @@ The session id.
.B \-autograb
enable autograb mode on \fBnxagent\fR startup. The autograb feature can be toggled via nxagent keystrokes
.TP 8
+.B \-textclipboard
+force text-only clipboard \fBnxagent\fR startup. See option file
+option \fBtextclipboard=<bool>\fR for an explanation.
+.TP 8
.B \-nxrealwindowprop
set property NX_REAL_WINDOW for each X11 client inside \fBnxagent\fR,
providing the window XID of the corresponding window object on the X
@@ -721,6 +725,15 @@ Limit clipboard data exchange to work only in one direction: from nxagent to rea
Disable any clipboard data exchange. Clipboard will still work inside the nxagent and on the real X server, but no data exchange will be possible.
.RE
.TP 8
+.B textclipboard=<bool>
+enable (set to \fI1\fR) or disable (set to \fI0\fR) text-only
+clipboard. Text-only clipboard is the old (<= 3.5.99.26) clipboard
+behaviour where you could only copy and paste text strings (no
+graphics, no rich text, ...). Using this mode been seen as a security feature
+as it effectively prevents transferring dangerous binary data,
+e.g. manipulated graphics by accident. On the other hand it's also
+less comfortable. (default: disabled)
+.TP 8
.B streaming=<bool>
enable (set to \fI1\fR) or disable (set to \fI0\fR) streaming support for images, not fully implemented yet and thus non-functional. (default: disabled)
.TP 8