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