diff --git a/dix/selection.c b/dix/selection.c index 7425b42c5..92e66ee16 100644 --- a/dix/selection.c +++ b/dix/selection.c @@ -66,6 +66,7 @@ SOFTWARE. Selection *CurrentSelections; CallbackListPtr SelectionCallback; +CallbackListPtr SelectionFilterCallback = NULL; int dixLookupSelection(Selection ** result, Atom selectionName, @@ -171,12 +172,27 @@ ProcSetSelectionOwner(ClientPtr client) if (CompareTimeStamps(time, currentTime) == LATER) return Success; - if (stuff->window != None) { - rc = dixLookupWindow(&pWin, stuff->window, client, DixSetAttrAccess); + /* allow extensions to intercept */ + SelectionFilterParamRec param = { + .client = client, + .selection = stuff->selection, + .owner = stuff->window, + .op = SELECTION_FILTER_SETOWNER, + }; + CallCallbacks(&SelectionFilterCallback, ¶m); + if (param.skip) { + if (param.status != Success) + client->errorValue = stuff->selection; + return param.status; + } + + if (param.owner != None) { + rc = dixLookupWindow(&pWin, param.owner, client, DixSetAttrAccess); if (rc != Success) return rc; } - if (!ValidAtom(stuff->selection)) { + + if (!ValidAtom(param.selection)) { client->errorValue = stuff->selection; return BadAtom; } @@ -184,10 +200,11 @@ ProcSetSelectionOwner(ClientPtr client) /* * First, see if the selection is already set... */ - rc = dixLookupSelection(&pSel, stuff->selection, client, DixSetAttrAccess); - - if (rc != Success) + rc = dixLookupSelection(&pSel, param.selection, client, DixSetAttrAccess); + if (rc != Success) { + client->errorValue = stuff->selection; return rc; + } /* If the timestamp in client's request is in the past relative to the time stamp indicating the last time the owner of the @@ -196,17 +213,27 @@ ProcSetSelectionOwner(ClientPtr client) if (CompareTimeStamps(time, pSel->lastTimeChanged) == EARLIER) return Success; if (pSel->client && (!pWin || (pSel->client != client))) { - xEvent event = { - .u.selectionClear.time = time.milliseconds, - .u.selectionClear.window = pSel->window, - .u.selectionClear.atom = pSel->selection + SelectionFilterParamRec eventParam = { + .client = client, + .recvClient = pSel->client, + .owner = pSel->window, + .selection = stuff->selection, + .op = SELECTION_FILTER_EV_CLEAR, }; - event.u.u.type = SelectionClear; - WriteEventsToClient(pSel->client, 1, &event); + CallCallbacks(&SelectionFilterCallback, &eventParam); + if (!param.skip) { + xEvent event = { + .u.selectionClear.time = time.milliseconds, + .u.selectionClear.window = eventParam.owner, + .u.selectionClear.atom = eventParam.selection, + }; + event.u.u.type = SelectionClear; + WriteEventsToClient(eventParam.recvClient, 1, &event); + } } pSel->lastTimeChanged = time; - pSel->window = stuff->window; + pSel->window = param.owner; pSel->pWin = pWin; pSel->client = (pWin ? client : NullClient); @@ -217,15 +244,25 @@ ProcSetSelectionOwner(ClientPtr client) int ProcGetSelectionOwner(ClientPtr client) { - int rc; Selection *pSel; REQUEST(xResourceReq); REQUEST_SIZE_MATCH(xResourceReq); - if (!ValidAtom(stuff->id)) { - client->errorValue = stuff->id; - return BadAtom; + /* allow extensions to intercept */ + SelectionFilterParamRec param = { + .client = client, + .selection = stuff->id, + .op = SELECTION_FILTER_GETOWNER, + }; + CallCallbacks(&SelectionFilterCallback, ¶m); + if (param.skip) { + goto out; + } + + if (!ValidAtom(param.selection)) { + param.status = BadAtom; + goto out; } xGetSelectionOwnerReply rep = { @@ -234,13 +271,13 @@ ProcGetSelectionOwner(ClientPtr client) .length = 0, }; - rc = dixLookupSelection(&pSel, stuff->id, client, DixGetAttrAccess); - if (rc == Success) + param.status = dixLookupSelection(&pSel, param.selection, param.client, DixGetAttrAccess); + if (param.status == Success) rep.owner = pSel->window; - else if (rc == BadMatch) + else if (param.status == BadMatch) rep.owner = None; else - return rc; + goto out; if (client->swapped) { swaps(&rep.sequenceNumber); @@ -249,6 +286,11 @@ ProcGetSelectionOwner(ClientPtr client) WriteToClient(client, sizeof(rep), &rep); return Success; + +out: + if (param.status != Success) + client->errorValue = stuff->id; + return param.status; } int @@ -263,12 +305,29 @@ ProcConvertSelection(ClientPtr client) REQUEST(xConvertSelectionReq); REQUEST_SIZE_MATCH(xConvertSelectionReq); - rc = dixLookupWindow(&pWin, stuff->requestor, client, DixSetAttrAccess); + /* allow extensions to intercept */ + SelectionFilterParamRec param = { + .client = client, + .selection = stuff->selection, + .op = SELECTION_FILTER_CONVERT, + .requestor = stuff->requestor, + .property = stuff->property, + .target = stuff->target, + .time = stuff->time, + }; + CallCallbacks(&SelectionFilterCallback, ¶m); + if (param.skip) { + if (param.status != Success) + client->errorValue = stuff->selection; + return param.status; + } + + rc = dixLookupWindow(&pWin, param.requestor, client, DixSetAttrAccess); if (rc != Success) return rc; - paramsOkay = ValidAtom(stuff->selection) && ValidAtom(stuff->target); - paramsOkay &= (stuff->property == None) || ValidAtom(stuff->property); + paramsOkay = ValidAtom(param.selection) && ValidAtom(param.target); + paramsOkay &= (param.property == None) || ValidAtom(param.property); if (!paramsOkay) { client->errorValue = stuff->property; return BadAtom; @@ -277,32 +336,63 @@ ProcConvertSelection(ClientPtr client) if (stuff->time == CurrentTime) UpdateCurrentTime(); - rc = dixLookupSelection(&pSel, stuff->selection, client, DixReadAccess); + rc = dixLookupSelection(&pSel, param.selection, client, DixReadAccess); memset(&event, 0, sizeof(xEvent)); if (rc != Success && rc != BadMatch) return rc; - else if (rc == Success && pSel->window != None) { - event.u.u.type = SelectionRequest; - event.u.selectionRequest.owner = pSel->window; - event.u.selectionRequest.time = stuff->time; - event.u.selectionRequest.requestor = stuff->requestor; - event.u.selectionRequest.selection = stuff->selection; - event.u.selectionRequest.target = stuff->target; - event.u.selectionRequest.property = stuff->property; - if (pSel->client && pSel->client != serverClient && - !pSel->client->clientGone) { - WriteEventsToClient(pSel->client, 1, &event); - return Success; + + /* If the specified selection has an owner, the X server sends + SelectionRequest event to that owner */ + if (rc == Success && pSel->window != None && pSel->client && + pSel->client != serverClient && !pSel->client->clientGone) + { + SelectionFilterParamRec evParam = { + .client = client, + .selection = stuff->selection, + .op = SELECTION_FILTER_EV_REQUEST, + .owner = pSel->window, + .requestor = stuff->requestor, + .property = stuff->property, + .target = stuff->target, + .time = stuff->time, + .recvClient = pSel->client, + }; + + CallCallbacks(&SelectionFilterCallback, &evParam); + if (evParam.skip) { + if (evParam.status != Success) + client->errorValue = stuff->selection; + return evParam.status; } + + event.u.u.type = SelectionRequest; + event.u.selectionRequest.owner = evParam.owner; + event.u.selectionRequest.time = evParam.time; + event.u.selectionRequest.requestor = evParam.requestor; + event.u.selectionRequest.selection = evParam.selection; + event.u.selectionRequest.target = evParam.target; + event.u.selectionRequest.property = evParam.property; + WriteEventsToClient(evParam.recvClient, 1, &event); + return Success; + } + + /* If no owner for the specified selection exists, the X server generates + a SelectionNotify event to the requestor with property None. */ + param.property = None; + CallCallbacks(&SelectionFilterCallback, ¶m); + if (param.skip) { + if (param.status != Success) + client->errorValue = stuff->selection; + return param.status; } event.u.u.type = SelectionNotify; - event.u.selectionNotify.time = stuff->time; - event.u.selectionNotify.requestor = stuff->requestor; - event.u.selectionNotify.selection = stuff->selection; - event.u.selectionNotify.target = stuff->target; - event.u.selectionNotify.property = None; + event.u.selectionNotify.time = param.time; + event.u.selectionNotify.requestor = param.requestor; + event.u.selectionNotify.selection = param.selection; + event.u.selectionNotify.target = param.target; + event.u.selectionNotify.property = param.property; WriteEventsToClient(client, 1, &event); return Success; } diff --git a/dix/selection_priv.h b/dix/selection_priv.h index 3eea9413e..24492d9ca 100644 --- a/dix/selection_priv.h +++ b/dix/selection_priv.h @@ -34,10 +34,32 @@ typedef struct { SelectionCallbackKind kind; } SelectionInfoRec; +#define SELECTION_FILTER_GETOWNER 1 +#define SELECTION_FILTER_SETOWNER 2 +#define SELECTION_FILTER_CONVERT 3 +#define SELECTION_FILTER_LISTEN 4 +#define SELECTION_FILTER_EV_REQUEST 5 +#define SELECTION_FILTER_EV_CLEAR 6 +#define SELECTION_FILTER_NOTIFY 7 + +typedef struct { + int op; + Bool skip; + int status; + Atom selection; + ClientPtr client; // initiating client + ClientPtr recvClient; // client receiving event + Time time; // request time stamp + Window requestor; + Window owner; + Atom property; + Atom target; +} SelectionFilterParamRec, *SelectionFilterParamPtr; extern Selection *CurrentSelections; extern CallbackListPtr SelectionCallback; +extern CallbackListPtr SelectionFilterCallback; int dixLookupSelection(Selection **result, Atom name, diff --git a/xfixes/select.c b/xfixes/select.c index 58fa514f3..e199934af 100644 --- a/xfixes/select.c +++ b/xfixes/select.c @@ -23,6 +23,7 @@ #include #include "dix/dix_priv.h" +#include "dix/selection_priv.h" #include "xfixesint.h" #include "xace.h" @@ -78,13 +79,25 @@ XFixesSelectionCallback(CallbackListPtr *callbacks, void *data, void *args) UpdateCurrentTimeIf(); for (e = selectionEvents; e; e = e->next) { if (e->selection == selection && (e->eventMask & eventMask)) { + + /* allow extensions to intercept */ + SelectionFilterParamRec param = { + .client = e->pClient, + .selection = selection->selection, + .owner = (subtype == XFixesSetSelectionOwnerNotify) ? + selection->window : 0, + .op = SELECTION_FILTER_NOTIFY, + }; + CallCallbacks(&SelectionFilterCallback, ¶m); + if (param.skip) + continue; + xXFixesSelectionNotifyEvent ev = { .type = XFixesEventBase + XFixesSelectionNotify, .subtype = subtype, .window = e->pWindow->drawable.id, - .owner = (subtype == XFixesSetSelectionOwnerNotify) ? - selection->window : 0, - .selection = e->selection->selection, + .owner = param.owner, + .selection = param.selection, .timestamp = currentTime.milliseconds, .selectionTimestamp = selection->lastTimeChanged.milliseconds }; @@ -122,8 +135,22 @@ ProcXFixesSelectSelectionInput(ClientPtr client) REQUEST(xXFixesSelectSelectionInputReq); REQUEST_SIZE_MATCH(xXFixesSelectSelectionInputReq); + /* allow extensions to intercept */ + SelectionFilterParamRec param = { + .client = client, + .selection = stuff->selection, + .owner = stuff->window, + .op = SELECTION_FILTER_LISTEN, + }; + CallCallbacks(&SelectionFilterCallback, ¶m); + if (param.skip) { + if (param.status != Success) + client->errorValue = param.selection; + return param.status; + } + WindowPtr pWindow; - int rc = dixLookupWindow(&pWindow, stuff->window, client, DixGetAttrAccess); + int rc = dixLookupWindow(&pWindow, param.owner, param.client, DixGetAttrAccess); if (rc != Success) return rc; if (stuff->eventMask & ~SelectionAllEvents) { @@ -135,13 +162,13 @@ ProcXFixesSelectSelectionInput(ClientPtr client) SelectionEventPtr *prev, e; Selection *selection; - rc = dixLookupSelection(&selection, stuff->selection, client, DixGetAttrAccess); + rc = dixLookupSelection(&selection, param.selection, param.client, DixGetAttrAccess); if (rc != Success) return rc; for (prev = &selectionEvents; (e = *prev); prev = &e->next) { if (e->selection == selection && - e->pClient == client && e->pWindow == pWindow) { + e->pClient == param.client && e->pWindow == pWindow) { break; } } @@ -158,9 +185,9 @@ ProcXFixesSelectSelectionInput(ClientPtr client) e->next = 0; e->selection = selection; - e->pClient = client; + e->pClient = param.client; e->pWindow = pWindow; - e->clientResource = FakeClientID(client->index); + e->clientResource = FakeClientID(param.client->index); /* * Add a resource hanging from the window to