/**************************************************************************/ /* */ /* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com) */ /* Copyright (c) 2008-2014 Oleksandr Shneyder */ /* Copyright (c) 2011-2016 Mike Gabriel */ /* Copyright (c) 2014-2016 Mihai Moldovan */ /* Copyright (c) 2014-2016 Ulrich Sibiller */ /* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com) */ /* */ /* NXAGENT, NX protocol compression and NX extensions to this software */ /* are copyright of the aforementioned persons and companies. */ /* */ /* Redistribution and use of the present software is allowed according */ /* to terms specified in the file LICENSE which comes in the source */ /* distribution. */ /* */ /* All rights reserved. */ /* */ /* NOTE: This software has received contributions from various other */ /* contributors, only the core maintainers and supporters are listed as */ /* copyright holders. Please contact us, if you feel you should be listed */ /* as copyright holder, as well. */ /* */ /**************************************************************************/ #include "X.h" #include "Xproto.h" #include "Xatom.h" #include "selection.h" #include "windowstr.h" #include "scrnintstr.h" #include "Windows.h" #include "Atoms.h" #include "Agent.h" #include "Args.h" #include "Trap.h" #include "Rootless.h" #include "Clipboard.h" #include "gcstruct.h" #include "xfixeswire.h" #include "X11/include/Xfixes_nxagent.h" /* * Use asyncronous get property replies. */ #include "compext/Compext.h" /* * Set here the required log level. */ #define PANIC #define WARNING #undef TEST #undef DEBUG /* * These are defined in the dispatcher. */ extern int NumCurrentSelections; extern Selection *CurrentSelections; int nxagentLastClipboardClient = -1; static int agentClipboardStatus; static int clientAccum; Atom serverCutProperty; Atom clientCutProperty; static Window serverWindow; static const int nxagentPrimarySelection = 0; static const int nxagentClipboardSelection = 1; static const int nxagentMaxSelections = 2; typedef struct _SelectionOwner { Atom selection; ClientPtr client; Window window; WindowPtr windowPtr; Time lastTimeChanged; } SelectionOwner; static SelectionOwner *lastSelectionOwner; static Atom nxagentLastRequestedSelection; static Atom nxagentClipboardAtom; static Atom nxagentTimestampAtom; /* * Needed to handle the notify selection event to * be sent to client once the selection property * has been retrieved from the real X server. */ typedef enum { SelectionStageNone, SelectionStageQuerySize, SelectionStageWaitSize, SelectionStageQueryData, 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; static Window lastServerRequestor; static Atom lastServerProperty; static Atom lastServerTarget; static Time lastServerTime; static Atom serverTARGETS; static Atom serverTEXT; static Atom serverUTF8_STRING; static Atom clientTARGETS; static Atom clientTEXT; static Atom clientCOMPOUND_TEXT; static Atom clientUTF8_STRING; static char szAgentTARGETS[] = "TARGETS"; static char szAgentTEXT[] = "TEXT"; static char szAgentCOMPOUND_TEXT[] = "COMPOUND_TEXT"; static char szAgentUTF8_STRING[] = "UTF8_STRING"; static char szAgentNX_CUT_BUFFER_CLIENT[] = "NX_CUT_BUFFER_CLIENT"; /* * Save the values queried from X server. */ XFixesAgentInfoRec nxagentXFixesInfo = { -1, -1, -1, 0 }; extern Display *nxagentDisplay; Bool nxagentValidServerTargets(Atom target); void nxagentSendSelectionNotify(Atom property); void nxagentTransferSelection(int resource); void nxagentCollectPropertyEvent(int resource); void nxagentResetSelectionOwner(void); WindowPtr nxagentGetClipboardWindow(Atom property, WindowPtr pWin); void nxagentNotifyConvertFailure(ClientPtr client, Window requestor, Atom selection, Atom target, Time time); int nxagentSendNotify(xEvent *event); /* * This is from NXproperty.c. */ int GetWindowProperty(WindowPtr pWin, Atom property, long longOffset, long longLength, Bool delete, Atom type, Atom *actualType, int *format, unsigned long *nItems, unsigned long *bytesAfter, unsigned char **propData); Bool nxagentValidServerTargets(Atom target) { #ifdef DEBUG fprintf(stderr, "nxagentValidServerTargets: Got called.\n"); #endif if (target == XA_STRING) return True; if (target == serverTEXT) return True; /* by dimbor */ if (target == serverUTF8_STRING) return True; return False; } void nxagentClearClipboard(ClientPtr pClient, WindowPtr pWindow) { int i; #ifdef DEBUG fprintf(stderr, "nxagentClearClipboard: Called with client [%p] window [%p].\n", (void *) pClient, (void *) pWindow); #endif /* * Only for PRIMARY and CLIPBOARD selections. */ for (i = 0; i < nxagentMaxSelections; i++) { if ((pClient != NULL && lastSelectionOwner[i].client == pClient) || (pWindow != NULL && lastSelectionOwner[i].windowPtr == pWindow)) { #ifdef TEST fprintf(stderr, "nxagentClearClipboard: Resetting state with client [%p] window [%p].\n", (void *) pClient, (void *) pWindow); #endif lastSelectionOwner[i].client = NULL; lastSelectionOwner[i].window = None; lastSelectionOwner[i].windowPtr = NULL; lastSelectionOwner[i].lastTimeChanged = GetTimeInMillis(); lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; lastServerRequestor = None; } } if (pWindow == lastClientWindowPtr) { lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; } } void nxagentClearSelection(XEvent *X) { int i = 0; #ifdef DEBUG fprintf(stderr, "nxagentClearSelection: Got called.\n"); #endif if (agentClipboardStatus != 1 || nxagentOption(Clipboard) == ClipboardServer) { return; } #ifdef DEBUG fprintf(stderr, "nxagentClearSelection: SelectionClear event.\n"); #endif while ((i < nxagentMaxSelections) && (lastSelectionOwner[i].selection != X->xselectionclear.selection)) { i++; } if (i < nxagentMaxSelections) { if (lastSelectionOwner[i].client != NULL) { xEvent x; memset(&x, 0, sizeof(xEvent)); 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); } 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(); } lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; } void nxagentRequestSelection(XEvent *X) { #ifdef DEBUG int result; #endif int i = 0; XSelectionEvent eventSelection; #ifdef DEBUG fprintf(stderr, "nxagentRequestSelection: Got called.\n"); #endif if (agentClipboardStatus != 1) { return; } 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))); fprintf(stderr, "SelectionRequest event aborting sele=[%s] ext target=[%s] Atom size is [%d]\n", validateString(NameForAtom(X->xselectionrequest.selection)), strTarget, sizeof(Atom)); if (strTarget != NULL) { XFree(strTarget); } */ memset(&eventSelection, 0, sizeof(XSelectionEvent)); eventSelection.property = None; 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; } if (X->xselectionrequest.target == nxagentTimestampAtom) { while ((i < NumCurrentSelections) && lastSelectionOwner[i].selection != X->xselectionrequest.selection) i++; if (i < NumCurrentSelections) { XChangeProperty(nxagentDisplay, X->xselectionrequest.requestor, X->xselectionrequest.property, X->xselectionrequest.target, 32, PropModeReplace, (unsigned char *) &lastSelectionOwner[i].lastTimeChanged, 1); eventSelection.property = X->xselectionrequest.property; } } 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 result = #endif XSendEvent(nxagentDisplay, eventSelection.requestor, False, 0L, (XEvent *) &eventSelection); #ifdef DEBUG if (result == BadValue || result == BadWindow) { fprintf(stderr, "nxagentRequestSelection: WARNING! XSendEvent failed.\n"); } else { fprintf(stderr, "nxagentRequestSelection: XSendEvent sent to window [0x%lx].\n", eventSelection.requestor); } #endif return; } /* * This is necessary in nxagentGetClipboardWindow. */ nxagentLastRequestedSelection = X->xselectionrequest.selection; while ((i < nxagentMaxSelections) && (lastSelectionOwner[i].selection != X->xselectionrequest.selection)) { i++; } if (i < nxagentMaxSelections) { if ((lastClientWindowPtr != NULL) && (lastSelectionOwner[i].client != NULL)) { XConvertSelection(nxagentDisplay, CurrentSelections[i].selection, X->xselectionrequest.target, serverCutProperty, serverWindow, lastClientTime); #ifdef DEBUG fprintf(stderr, "nxagentRequestSelection: Sent XConvertSelection.\n"); #endif } else { if (lastSelectionOwner[i].client != NULL && nxagentOption(Clipboard) != ClipboardClient) { xEvent x; lastServerProperty = X->xselectionrequest.property; lastServerRequestor = X->xselectionrequest.requestor; lastServerTarget = X->xselectionrequest.target; /* by dimbor */ if (lastServerTarget != XA_STRING) lastServerTarget = serverUTF8_STRING; lastServerTime = X->xselectionrequest.time; memset(&x, 0, sizeof(xEvent)); x.u.u.type = SelectionRequest; x.u.selectionRequest.time = GetTimeInMillis(); x.u.selectionRequest.owner = lastSelectionOwner[i].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. * * 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); #ifdef DEBUG fprintf(stderr, "nxagentRequestSelection: Executed TryClientEvents with clientCutProperty.\n"); #endif } else { /* * Probably we must to 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 result = #endif XSendEvent(nxagentDisplay, eventSelection.requestor, False, 0L, (XEvent *) &eventSelection); #ifdef DEBUG fprintf(stderr, "nxagentRequestSelection: Executed XSendEvent with property None.\n"); #endif } } } } void nxagentSendSelectionNotify(Atom property) { xEvent x; #ifdef DEBUG fprintf (stderr, "nxagentSendSelectionNotify: Sending event to client [%d].\n", lastClientClientPtr -> index); #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; TryClientEvents(lastClientClientPtr, &x, 1, NoEventMask, NoEventMask , NullGrab); return; } void nxagentTransferSelection(int resource) { int result; if (lastClientClientPtr -> index != resource) { #ifdef DEBUG fprintf (stderr, "nxagentTransferSelection: WARNING! Inconsistent resource [%d] with current client [%d].\n", resource, lastClientClientPtr -> index); #endif nxagentSendSelectionNotify(None); lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; return; } switch (lastClientStage) { case SelectionStageQuerySize: { /* * Don't get data yet, just get size. We skip * this stage in current implementation and * go straight to the data. */ nxagentLastClipboardClient = NXGetCollectPropertyResource(nxagentDisplay); if (nxagentLastClipboardClient == -1) { #ifdef WARNING fprintf(stderr, "nxagentTransferSelection: WARNING! Asynchronous GetProperty queue full.\n"); #endif result = -1; } else { result = NXCollectProperty(nxagentDisplay, nxagentLastClipboardClient, serverWindow, serverCutProperty, 0, 0, False, AnyPropertyType); } if (result == -1) { #ifdef DEBUG fprintf (stderr, "nxagentTransferSelection: Aborting selection notify procedure for client [%d].\n", lastClientClientPtr -> index); #endif nxagentSendSelectionNotify(None); lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; return; } #ifdef DEBUG fprintf (stderr, "nxagentTransferSelection: Setting stage to [%d] for client [%d].\n", SelectionStageWaitSize, lastClientClientPtr -> index); #endif lastClientStage = SelectionStageWaitSize; break; } case SelectionStageQueryData: { /* * Request the selection data now. */ #ifdef DEBUG fprintf(stderr, "nxagentTransferSelection: Getting property content from remote server.\n"); #endif nxagentLastClipboardClient = NXGetCollectPropertyResource(nxagentDisplay); if (nxagentLastClipboardClient == -1) { #ifdef WARNING fprintf(stderr, "nxagentTransferSelection: WARNING! Asynchronous GetProperty queue full.\n"); #endif result = -1; } else { result = NXCollectProperty(nxagentDisplay, nxagentLastClipboardClient, serverWindow, serverCutProperty, 0, lastClientPropertySize, False, AnyPropertyType); } if (result == -1) { #ifdef DEBUG fprintf (stderr, "nxagentTransferSelection: Aborting selection notify procedure for client [%d].\n", lastClientClientPtr -> index); #endif nxagentSendSelectionNotify(None); lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; return; } #ifdef DEBUG fprintf (stderr, "nxagentTransferSelection: Setting stage to [%d] for client [%d].\n", SelectionStageWaitData, lastClientClientPtr -> index); #endif lastClientStage = SelectionStageWaitData; break; } default: { #ifdef DEBUG fprintf (stderr, "nxagentTransferSelection: WARNING! Inconsistent state [%d] for client [%d].\n", lastClientStage, lastClientClientPtr -> index); #endif break; } } } void nxagentCollectPropertyEvent(int resource) { Atom atomReturnType; int resultFormat; unsigned long ulReturnItems; unsigned long ulReturnBytesLeft; unsigned char *pszReturnData = NULL; int result; /* * We have received the notification so * we can safely retrieve data from the * client structure. */ result = NXGetCollectedProperty(nxagentDisplay, resource, &atomReturnType, &resultFormat, &ulReturnItems, &ulReturnBytesLeft, &pszReturnData); nxagentLastClipboardClient = -1; if (result == 0) { #ifdef DEBUG fprintf (stderr, "nxagentCollectPropertyEvent: Failed to get reply data for client [%d].\n", lastClientClientPtr -> index); #endif nxagentSendSelectionNotify(None); lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; if (pszReturnData != NULL) { XFree(pszReturnData); } return; } if (resultFormat != 8 && resultFormat != 16 && resultFormat != 32) { #ifdef DEBUG fprintf (stderr, "nxagentCollectPropertyEvent: WARNING! Invalid property " "value.\n"); #endif if (lastClientClientPtr != NULL) { nxagentSendSelectionNotify(None); } lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; if (pszReturnData != NULL) { XFree(pszReturnData); } return; } switch (lastClientStage) { case SelectionStageWaitSize: { #ifdef DEBUG fprintf (stderr, "nxagentCollectPropertyEvent: Got size notify event for client [%d].\n", lastClientClientPtr -> index); #endif if (ulReturnBytesLeft == 0) { #ifdef DEBUG fprintf (stderr, "nxagentCollectPropertyEvent: Aborting selection notify procedure for client [%d].\n", lastClientClientPtr -> index); #endif nxagentSendSelectionNotify(None); lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; if (pszReturnData != NULL) { free(pszReturnData); } return; } #ifdef DEBUG fprintf(stderr, "nxagentCollectPropertyEvent: Got property size from remote server.\n"); #endif /* * Request the selection data now. */ lastClientPropertySize = ulReturnBytesLeft; lastClientStage = SelectionStageQueryData; nxagentTransferSelection(resource); break; } case SelectionStageWaitData: { #ifdef DEBUG fprintf (stderr, "nxagentCollectPropertyEvent: Got data notify event for client [%d].\n", lastClientClientPtr -> index); #endif if (ulReturnBytesLeft != 0) { #ifdef DEBUG fprintf (stderr, "nxagentCollectPropertyEvent: Aborting selection notify procedure for client [%d].\n", lastClientClientPtr -> index); #endif nxagentSendSelectionNotify(None); lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; if (pszReturnData != NULL) { free(pszReturnData); } return; } #ifdef DEBUG fprintf(stderr, "nxagentCollectPropertyEvent: Got property content from remote server.\n"); #endif ChangeWindowProperty(lastClientWindowPtr, lastClientProperty, lastClientTarget, resultFormat, PropModeReplace, ulReturnItems, pszReturnData, 1); #ifdef DEBUG fprintf(stderr, "nxagentCollectPropertyEvent: Selection property [%s] changed to [%s]\n", validateString(NameForAtom(lastClientProperty)), pszReturnData); #endif nxagentSendSelectionNotify(lastClientProperty); /* * Enable further requests from clients. */ lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; break; } default: { #ifdef DEBUG fprintf (stderr, "nxagentCollectPropertyEvent: WARNING! Inconsistent state [%d] for client [%d].\n", lastClientStage, lastClientClientPtr -> index); #endif break; } } XFree(pszReturnData); pszReturnData = NULL; } void nxagentNotifySelection(XEvent *X) { int result; XSelectionEvent eventSelection; #ifdef DEBUG fprintf(stderr, "nxagentNotifySelection: Got called.\n"); #endif if (agentClipboardStatus != 1) { return; } #ifdef DEBUG fprintf(stderr, "nxagentNotifySelection: SelectionNotify event.\n"); #endif if (lastClientWindowPtr != NULL) { if ((lastClientStage == SelectionStageNone) && (X->xselection.property == serverCutProperty)) { #ifdef DEBUG fprintf(stderr, "nxagentNotifySelection: Starting selection transferral for client [%d].\n", lastClientClientPtr -> index); #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. */ lastClientStage = SelectionStageQueryData; lastClientPropertySize = 262144; nxagentTransferSelection(lastClientClientPtr -> index); } else { #ifdef DEBUG fprintf(stderr, "nxagentNotifySelection: WARNING! Resetting selection transferral for client [%d].\n", lastClientClientPtr -> index); #endif nxagentSendSelectionNotify(None); lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; } return; } else { int i = 0; while ((i < nxagentMaxSelections) && (lastSelectionOwner[i].selection != X->xselection.selection)) { i++; } if (i < nxagentMaxSelections) { if ((lastSelectionOwner[i].client != NULL) && (lastSelectionOwner[i].windowPtr != NULL) && (X->xselection.property == clientCutProperty)) { Atom atomReturnType; int resultFormat; unsigned long ulReturnItems; unsigned long ulReturnBytesLeft; unsigned char *pszReturnData = NULL; result = GetWindowProperty(lastSelectionOwner[i].windowPtr, clientCutProperty, 0, 0, False, AnyPropertyType, &atomReturnType, &resultFormat, &ulReturnItems, &ulReturnBytesLeft, &pszReturnData); if (result == BadAlloc || result == BadAtom || result == BadMatch || result == BadValue || result == BadWindow) { fprintf (stderr, "Client GetProperty failed error ="); lastServerProperty = None; switch (result) { case BadAtom: fprintf (stderr, "BadAtom\n"); break; case BadValue: fprintf (stderr, "BadValue\n"); break; case BadWindow: fprintf (stderr, "BadWindow\n"); break; } } else { result = GetWindowProperty(lastSelectionOwner[i].windowPtr, clientCutProperty, 0, ulReturnBytesLeft, False, AnyPropertyType, &atomReturnType, &resultFormat, &ulReturnItems, &ulReturnBytesLeft, &pszReturnData); if (result == BadAlloc || result == BadAtom || result == BadMatch || result == BadValue || result == BadWindow) { fprintf (stderr, "SelectionNotify - XChangeProperty failed\n"); lastServerProperty = None; } else { result = XChangeProperty(nxagentDisplay, lastServerRequestor, lastServerProperty, lastServerTarget, 8, PropModeReplace, pszReturnData, ulReturnItems); } /* * if (pszReturnData) * { * free(pszReturnData); * pszReturnData=NULL; * } */ } 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; */ eventSelection.target = lastServerTarget; eventSelection.property = lastServerProperty; eventSelection.time = lastServerTime; /* * eventSelection.time = CurrentTime; * eventSelection.time = lastServerTime; */ #ifdef DEBUG fprintf(stderr, "nxagentNotifySelection: Sending event to requestor.\n"); #endif result = XSendEvent(nxagentDisplay, eventSelection.requestor, False, 0L, (XEvent *) &eventSelection); if (result == BadValue || result == BadWindow) { fprintf (stderr, "SelectionRequest - XSendEvent failed\n"); } lastServerRequestor = None; /* allow further request */ } } } } /* * Acquire selection so we don't get selection * requests from real X clients. */ void nxagentResetSelectionOwner() { int i; if (lastServerRequestor != None) { #ifdef TEST fprintf (stderr, "nxagentResetSelectionOwner: WARNING! Requestor window [0x%lx] already found.\n", lastServerRequestor); #endif return; } /* * Only for PRIMARY and CLIPBOARD selections. */ for (i = 0; i < nxagentMaxSelections; i++) { XSetSelectionOwner(nxagentDisplay, lastSelectionOwner[i].selection, serverWindow, CurrentTime); fprintf (stderr, "nxagentResetSelectionOwner: Reset clipboard state.\n"); lastSelectionOwner[i].client = NULL; lastSelectionOwner[i].window = None; lastSelectionOwner[i].windowPtr = NULL; lastSelectionOwner[i].lastTimeChanged = GetTimeInMillis(); } lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; lastServerRequestor = None; return; } void nxagentSetSelectionOwner(Selection *pSelection) { int i; #ifdef DEBUG fprintf(stderr, "nxagentSetSelectionOwner: Got called.\n"); #endif if (agentClipboardStatus != 1) { return; } #ifdef DEBUG fprintf(stderr, "nxagentSetSelectionOwner: Setting selection owner to window [0x%lx].\n", serverWindow); #endif #ifdef TEST if (lastServerRequestor != None) { fprintf (stderr, "nxagentSetSelectionOwner: WARNING! Requestor window [0x%lx] already found.\n", lastServerRequestor); } #endif /* * Only for PRIMARY and CLIPBOARD selections. */ for (i = 0; i < nxagentMaxSelections; i++) { if (pSelection->selection == CurrentSelections[i].selection) { 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(); } } lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; lastServerRequestor = None; /* FIXME if (XGetSelectionOwner(nxagentDisplay,pSelection->selection)==serverWindow) { fprintf (stderr, "NXdispatch: SetSelectionOwner OK\n"); lastSelectionOwnerSelection = pSelection; lastSelectionOwnerClient = pSelection->client; lastSelectionOwnerWindow = pSelection->window; lastSelectionOwnerWindowPtr = pSelection->pWin; lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; lastServerRequestor = None; } else fprintf (stderr, "nxagentSetSelectionOwner: SetSelectionOwner failed\n"); */ } 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? */ if (clients[client -> index] != client) { #ifdef WARNING fprintf(stderr, "nxagentNotifyConvertFailure: WARNING! Invalid client pointer."); #endif 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); } 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++) { if ((selection == CurrentSelections[i].selection) && (lastSelectionOwner[i].client != NULL)) { return 0; } } if (lastClientWindowPtr != NULL) { #ifdef TEST fprintf(stderr, "nxagentConvertSelection: lastClientWindowPtr != NULL.\n"); #endif if ((GetTimeInMillis() - lastClientReqTime) > 5000) { #ifdef DEBUG fprintf(stderr, "nxagentConvertSelection: timeout expired on last request, " "notifying failure to client\n"); #endif nxagentNotifyConvertFailure(lastClientClientPtr, lastClientRequestor, lastClientSelection, lastClientTarget, lastClientTime); lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; } else { #ifdef DEBUG fprintf(stderr, "nxagentConvertSelection: got request " "before timeout expired on last request, notifying failure to client\n"); #endif nxagentNotifyConvertFailure(client, requestor, selection, target, time); return 1; } } #ifdef TEST fprintf(stderr, "nxagentConvertSelection: client [%d] ask for sel [%s] " "on window [%lx] prop [%s] target [%s].\n", client -> index, validateString(NameForAtom(selection)), requestor, validateString(NameForAtom(property)), validateString(NameForAtom(target))); #endif strTarget = NameForAtom(target); if (strTarget == NULL) { return 1; } 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; ChangeWindowProperty(pWin, property, MakeAtom("ATOM", 4, 1), sizeof(Atom)*8, PropModeReplace, 4, &xa_STRING, 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); return 1; } if (target == MakeAtom("TIMESTAMP", 9, 1)) { xEvent x; int i = 0; while ((i < NumCurrentSelections) && CurrentSelections[i].selection != selection) i++; if (i < NumCurrentSelections) { ChangeWindowProperty(pWin, property, target, 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); return 1; } } if (lastClientClientPtr == client && (GetTimeInMillis() - lastClientReqTime < 5000)) { /* * The same client made consecutive requests * of clipboard contents with less than 5 * seconds time interval between them. */ #ifdef DEBUG fprintf(stderr, "nxagentConvertSelection: Consecutives request from client [%p] selection [%ld] " "elapsed time [%lu] clientAccum [%d]\n", (void *) client, selection, GetTimeInMillis() - lastClientReqTime, clientAccum); #endif clientAccum++; } else { if (lastClientClientPtr != client) { clientAccum = 0; } } if ((target == clientTEXT) || (target == XA_STRING) || (target == clientCOMPOUND_TEXT) || (target == clientUTF8_STRING)) { lastClientWindowPtr = pWin; lastClientStage = SelectionStageNone; lastClientRequestor = requestor; lastClientClientPtr = client; lastClientTime = time; lastClientProperty = property; lastClientSelection = selection; lastClientTarget = target; lastClientReqTime = (GetTimeInMillis() - lastClientReqTime) > 5000 ? GetTimeInMillis() : lastClientReqTime; if (selection == MakeAtom("CLIPBOARD", 9, 0)) { selection = lastSelectionOwner[nxagentClipboardSelection].selection; } if (target == clientUTF8_STRING) { XConvertSelection(nxagentDisplay, selection, serverUTF8_STRING, serverCutProperty, serverWindow, CurrentTime); } else { XConvertSelection(nxagentDisplay, selection, XA_STRING, serverCutProperty, serverWindow, CurrentTime); } #ifdef DEBUG fprintf(stderr, "nxagentConvertSelection: Sent XConvertSelection with target=[%s], property [%s]\n", validateString(NameForAtom(target)), validateString(NameForAtom(property))); #endif return 1; } else { xEvent x; #ifdef DEBUG fprintf(stderr, "nxagentConvertSelection: Xserver generates a SelectionNotify event " "to the requestor with property None.\n"); #endif memset(&x, 0, sizeof(xEvent)); x.u.u.type = SelectionNotify; x.u.selectionNotify.time = time; x.u.selectionNotify.requestor = requestor; x.u.selectionNotify.selection = selection; x.u.selectionNotify.target = target; x.u.selectionNotify.property = None; (void) TryClientEvents(client, &x, 1, NoEventMask, NoEventMask , NullGrab); return 1; } return 0; } int nxagentSendNotify(xEvent *event) { #ifdef DEBUG fprintf(stderr, "nxagentSendNotify: Got called.\n"); #endif if (agentClipboardStatus != 1) { return 0; } if (event->u.selectionNotify.property == clientCutProperty) { XSelectionEvent x; int result; /* * Setup selection notify event to real server. */ memset(&x, 0, sizeof(XSelectionEvent)); x.type = SelectionNotify; x.send_event = True; x.display = nxagentDisplay; x.requestor = serverWindow; /* * On real server, the right CLIPBOARD atom is * XInternAtom(nxagentDisplay, "CLIPBOARD", 1). */ if (event->u.selectionNotify.selection == MakeAtom("CLIPBOARD", 9, 0)) { x.selection = lastSelectionOwner[nxagentClipboardSelection].selection; } else { x.selection = event->u.selectionNotify.selection; } x.target = event->u.selectionNotify.target; x.property = event->u.selectionNotify.property; x.time = CurrentTime; #ifdef DEBUG fprintf(stderr, "nxagentSendNotify: Propagating clientCutProperty.\n"); #endif result = XSendEvent (nxagentDisplay, x.requestor, False, 0L, (XEvent *) &x); if (result == BadValue || result == BadWindow) { fprintf (stderr, "nxagentSendNotify: XSendEvent failed.\n"); } return 1; } return 0; } WindowPtr nxagentGetClipboardWindow(Atom property, WindowPtr pWin) { int i = 0; #ifdef DEBUG fprintf(stderr, "nxagentGetClipboardWindow: Got called.\n"); #endif while ((i < nxagentMaxSelections) && (lastSelectionOwner[i].selection != nxagentLastRequestedSelection)) { i++; } if ((i < nxagentMaxSelections) && (property == clientCutProperty) && (lastSelectionOwner[i].windowPtr != NULL)) { #ifdef DEBUG fprintf(stderr, "nxagentGetClipboardWindow: Returning last clipboard owner window.\n"); #endif return lastSelectionOwner[i].windowPtr; } else { #ifdef DEBUG fprintf(stderr, "nxagentGetClipboardWindow: Returning original target window.\n"); #endif return pWin; } } int nxagentInitClipboard(WindowPtr pWin) { int i; Window iWindow = nxagentWindow(pWin); #ifdef DEBUG fprintf(stderr, "nxagentInitClipboard: Got called.\n"); #endif if (lastSelectionOwner != NULL) { free(lastSelectionOwner); lastSelectionOwner = NULL; } lastSelectionOwner = (SelectionOwner *) malloc(2 * sizeof(SelectionOwner)); if (lastSelectionOwner == NULL) { 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(); lastSelectionOwner[nxagentClipboardSelection].selection = nxagentClipboardAtom; lastSelectionOwner[nxagentClipboardSelection].client = NullClient; lastSelectionOwner[nxagentClipboardSelection].window = screenInfo.screens[0]->root->drawable.id; lastSelectionOwner[nxagentClipboardSelection].windowPtr = NULL; lastSelectionOwner[nxagentClipboardSelection].lastTimeChanged = GetTimeInMillis(); #ifdef NXAGENT_TIMESTAMP { extern unsigned long startTime; fprintf(stderr, "nxagentInitClipboard: Initializing start [%d] milliseconds.\n", GetTimeInMillis() - startTime); } #endif agentClipboardStatus = 0; serverWindow = iWindow; /* * Local property to hold pasted data. */ serverCutProperty = nxagentAtoms[5]; /* NX_CUT_BUFFER_SERVER */ serverTARGETS = nxagentAtoms[6]; /* TARGETS */ serverTEXT = nxagentAtoms[7]; /* TEXT */ serverUTF8_STRING = nxagentAtoms[12]; /* UTF8_STRING */ if (serverCutProperty == None) { #ifdef PANIC fprintf(stderr, "nxagentInitClipboard: PANIC! Could not create NX_CUT_BUFFER_SERVER atom\n"); #endif return -1; } #ifdef TEST fprintf(stderr, "nxagentInitClipboard: Setting owner of selection [%s][%d] on window 0x%lx\n", "NX_CUT_BUFFER_SERVER", (int) serverCutProperty, iWindow); #endif XSetSelectionOwner(nxagentDisplay, serverCutProperty, iWindow, CurrentTime); if (XQueryExtension(nxagentDisplay, "XFIXES", &nxagentXFixesInfo.Opcode, &nxagentXFixesInfo.EventBase, &nxagentXFixesInfo.ErrorBase) == 0) { ErrorF("Unable to initialize XFixes extension.\n"); } else { #ifdef TEST fprintf(stderr, "nxagentInitClipboard: Registering for XFixesSelectionNotify events.\n"); #endif for (i = 0; i < nxagentMaxSelections; i++) { XFixesSelectSelectionInput(nxagentDisplay, iWindow, lastSelectionOwner[i].selection, XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask); } nxagentXFixesInfo.Initialized = 1; } /* The first paste from CLIPBOARD did not work directly after session start. Removing this code makes it work. It is unsure why it was introduced in the first place so it is possible that we see other effects by leaving out this code. Fixes X2Go bug #952, see https://bugs.x2go.org/952 for details . if (nxagentSessionId[0]) { #ifdef TEST fprintf(stderr, "nxagentInitClipboard: setting the ownership of %s to %lx" " and registering for PropertyChangeMask events\n", validateString(XGetAtomName(nxagentDisplay, nxagentAtoms[10])), iWindow); #endif XSetSelectionOwner(nxagentDisplay, nxagentAtoms[10], iWindow, CurrentTime); pWin -> eventMask |= PropertyChangeMask; nxagentChangeWindowAttributes(pWin, CWEventMask); } */ if (nxagentReconnectTrap) { /* * Only for PRIMARY and CLIPBOARD selections. */ for (i = 0; i < nxagentMaxSelections; i++) { if (lastSelectionOwner[i].client && lastSelectionOwner[i].window) { XSetSelectionOwner(nxagentDisplay, lastSelectionOwner[i].selection, iWindow, CurrentTime); } } } else { lastSelectionOwner[nxagentPrimarySelection].client = NULL; lastSelectionOwner[nxagentClipboardSelection].client = NULL; lastServerRequestor = None; lastClientWindowPtr = NULL; lastClientStage = SelectionStageNone; lastClientReqTime = GetTimeInMillis(); clientCutProperty = MakeAtom(szAgentNX_CUT_BUFFER_CLIENT, strlen(szAgentNX_CUT_BUFFER_CLIENT), 1); 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); if (clientCutProperty == None) { #ifdef PANIC fprintf(stderr, "nxagentInitClipboard: PANIC! " "Could not create NX_CUT_BUFFER_CLIENT atom.\n"); #endif return -1; } } agentClipboardStatus = 1; #ifdef DEBUG fprintf(stderr, "nxagentInitClipboard: Clipboard initialization completed.\n"); #endif #ifdef NXAGENT_TIMESTAMP { extern unsigned long startTime; fprintf(stderr, "nxagentInitClipboard: initializing ends [%d] milliseconds.\n", GetTimeInMillis() - startTime); } #endif return 1; }