dix: add selection filtering hooks
Add hooks for filtering and fully rewrite rewrite selection requests and events (what existing XACE hooks cannot do), e.g. for supporting separate selection name spaces. The hook can change individual fields in the parameter struct, so operation continues with these changed values (eg. replace the original selection name atom by a different one). It's also possible to stop operations completely (with given result code) - in that case the hook needs to take care of the remaining work to do (eg. sending events) Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
This commit is contained in:
parent
e6467895f9
commit
16215b98d2
164
dix/selection.c
164
dix/selection.c
|
@ -66,6 +66,7 @@ SOFTWARE.
|
||||||
|
|
||||||
Selection *CurrentSelections;
|
Selection *CurrentSelections;
|
||||||
CallbackListPtr SelectionCallback;
|
CallbackListPtr SelectionCallback;
|
||||||
|
CallbackListPtr SelectionFilterCallback = NULL;
|
||||||
|
|
||||||
int
|
int
|
||||||
dixLookupSelection(Selection ** result, Atom selectionName,
|
dixLookupSelection(Selection ** result, Atom selectionName,
|
||||||
|
@ -171,12 +172,27 @@ ProcSetSelectionOwner(ClientPtr client)
|
||||||
if (CompareTimeStamps(time, currentTime) == LATER)
|
if (CompareTimeStamps(time, currentTime) == LATER)
|
||||||
return Success;
|
return Success;
|
||||||
|
|
||||||
if (stuff->window != None) {
|
/* allow extensions to intercept */
|
||||||
rc = dixLookupWindow(&pWin, stuff->window, client, DixSetAttrAccess);
|
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)
|
if (rc != Success)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
if (!ValidAtom(stuff->selection)) {
|
|
||||||
|
if (!ValidAtom(param.selection)) {
|
||||||
client->errorValue = stuff->selection;
|
client->errorValue = stuff->selection;
|
||||||
return BadAtom;
|
return BadAtom;
|
||||||
}
|
}
|
||||||
|
@ -184,10 +200,11 @@ ProcSetSelectionOwner(ClientPtr client)
|
||||||
/*
|
/*
|
||||||
* First, see if the selection is already set...
|
* First, see if the selection is already set...
|
||||||
*/
|
*/
|
||||||
rc = dixLookupSelection(&pSel, stuff->selection, client, DixSetAttrAccess);
|
rc = dixLookupSelection(&pSel, param.selection, client, DixSetAttrAccess);
|
||||||
|
if (rc != Success) {
|
||||||
if (rc != Success)
|
client->errorValue = stuff->selection;
|
||||||
return rc;
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/* If the timestamp in client's request is in the past relative
|
/* If the timestamp in client's request is in the past relative
|
||||||
to the time stamp indicating the last time the owner of the
|
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)
|
if (CompareTimeStamps(time, pSel->lastTimeChanged) == EARLIER)
|
||||||
return Success;
|
return Success;
|
||||||
if (pSel->client && (!pWin || (pSel->client != client))) {
|
if (pSel->client && (!pWin || (pSel->client != client))) {
|
||||||
|
SelectionFilterParamRec eventParam = {
|
||||||
|
.client = client,
|
||||||
|
.recvClient = pSel->client,
|
||||||
|
.owner = pSel->window,
|
||||||
|
.selection = stuff->selection,
|
||||||
|
.op = SELECTION_FILTER_EV_CLEAR,
|
||||||
|
};
|
||||||
|
CallCallbacks(&SelectionFilterCallback, &eventParam);
|
||||||
|
if (!param.skip) {
|
||||||
xEvent event = {
|
xEvent event = {
|
||||||
.u.selectionClear.time = time.milliseconds,
|
.u.selectionClear.time = time.milliseconds,
|
||||||
.u.selectionClear.window = pSel->window,
|
.u.selectionClear.window = eventParam.owner,
|
||||||
.u.selectionClear.atom = pSel->selection
|
.u.selectionClear.atom = eventParam.selection,
|
||||||
};
|
};
|
||||||
event.u.u.type = SelectionClear;
|
event.u.u.type = SelectionClear;
|
||||||
WriteEventsToClient(pSel->client, 1, &event);
|
WriteEventsToClient(eventParam.recvClient, 1, &event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pSel->lastTimeChanged = time;
|
pSel->lastTimeChanged = time;
|
||||||
pSel->window = stuff->window;
|
pSel->window = param.owner;
|
||||||
pSel->pWin = pWin;
|
pSel->pWin = pWin;
|
||||||
pSel->client = (pWin ? client : NullClient);
|
pSel->client = (pWin ? client : NullClient);
|
||||||
|
|
||||||
|
@ -217,15 +244,25 @@ ProcSetSelectionOwner(ClientPtr client)
|
||||||
int
|
int
|
||||||
ProcGetSelectionOwner(ClientPtr client)
|
ProcGetSelectionOwner(ClientPtr client)
|
||||||
{
|
{
|
||||||
int rc;
|
|
||||||
Selection *pSel;
|
Selection *pSel;
|
||||||
|
|
||||||
REQUEST(xResourceReq);
|
REQUEST(xResourceReq);
|
||||||
REQUEST_SIZE_MATCH(xResourceReq);
|
REQUEST_SIZE_MATCH(xResourceReq);
|
||||||
|
|
||||||
if (!ValidAtom(stuff->id)) {
|
/* allow extensions to intercept */
|
||||||
client->errorValue = stuff->id;
|
SelectionFilterParamRec param = {
|
||||||
return BadAtom;
|
.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 = {
|
xGetSelectionOwnerReply rep = {
|
||||||
|
@ -234,13 +271,13 @@ ProcGetSelectionOwner(ClientPtr client)
|
||||||
.length = 0,
|
.length = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
rc = dixLookupSelection(&pSel, stuff->id, client, DixGetAttrAccess);
|
param.status = dixLookupSelection(&pSel, param.selection, param.client, DixGetAttrAccess);
|
||||||
if (rc == Success)
|
if (param.status == Success)
|
||||||
rep.owner = pSel->window;
|
rep.owner = pSel->window;
|
||||||
else if (rc == BadMatch)
|
else if (param.status == BadMatch)
|
||||||
rep.owner = None;
|
rep.owner = None;
|
||||||
else
|
else
|
||||||
return rc;
|
goto out;
|
||||||
|
|
||||||
if (client->swapped) {
|
if (client->swapped) {
|
||||||
swaps(&rep.sequenceNumber);
|
swaps(&rep.sequenceNumber);
|
||||||
|
@ -249,6 +286,11 @@ ProcGetSelectionOwner(ClientPtr client)
|
||||||
|
|
||||||
WriteToClient(client, sizeof(rep), &rep);
|
WriteToClient(client, sizeof(rep), &rep);
|
||||||
return Success;
|
return Success;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (param.status != Success)
|
||||||
|
client->errorValue = stuff->id;
|
||||||
|
return param.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -263,12 +305,29 @@ ProcConvertSelection(ClientPtr client)
|
||||||
REQUEST(xConvertSelectionReq);
|
REQUEST(xConvertSelectionReq);
|
||||||
REQUEST_SIZE_MATCH(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)
|
if (rc != Success)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
paramsOkay = ValidAtom(stuff->selection) && ValidAtom(stuff->target);
|
paramsOkay = ValidAtom(param.selection) && ValidAtom(param.target);
|
||||||
paramsOkay &= (stuff->property == None) || ValidAtom(stuff->property);
|
paramsOkay &= (param.property == None) || ValidAtom(param.property);
|
||||||
if (!paramsOkay) {
|
if (!paramsOkay) {
|
||||||
client->errorValue = stuff->property;
|
client->errorValue = stuff->property;
|
||||||
return BadAtom;
|
return BadAtom;
|
||||||
|
@ -277,32 +336,63 @@ ProcConvertSelection(ClientPtr client)
|
||||||
if (stuff->time == CurrentTime)
|
if (stuff->time == CurrentTime)
|
||||||
UpdateCurrentTime();
|
UpdateCurrentTime();
|
||||||
|
|
||||||
rc = dixLookupSelection(&pSel, stuff->selection, client, DixReadAccess);
|
rc = dixLookupSelection(&pSel, param.selection, client, DixReadAccess);
|
||||||
|
|
||||||
memset(&event, 0, sizeof(xEvent));
|
memset(&event, 0, sizeof(xEvent));
|
||||||
if (rc != Success && rc != BadMatch)
|
if (rc != Success && rc != BadMatch)
|
||||||
return rc;
|
return rc;
|
||||||
else if (rc == Success && pSel->window != None) {
|
|
||||||
|
/* 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.u.type = SelectionRequest;
|
||||||
event.u.selectionRequest.owner = pSel->window;
|
event.u.selectionRequest.owner = evParam.owner;
|
||||||
event.u.selectionRequest.time = stuff->time;
|
event.u.selectionRequest.time = evParam.time;
|
||||||
event.u.selectionRequest.requestor = stuff->requestor;
|
event.u.selectionRequest.requestor = evParam.requestor;
|
||||||
event.u.selectionRequest.selection = stuff->selection;
|
event.u.selectionRequest.selection = evParam.selection;
|
||||||
event.u.selectionRequest.target = stuff->target;
|
event.u.selectionRequest.target = evParam.target;
|
||||||
event.u.selectionRequest.property = stuff->property;
|
event.u.selectionRequest.property = evParam.property;
|
||||||
if (pSel->client && pSel->client != serverClient &&
|
WriteEventsToClient(evParam.recvClient, 1, &event);
|
||||||
!pSel->client->clientGone) {
|
|
||||||
WriteEventsToClient(pSel->client, 1, &event);
|
|
||||||
return Success;
|
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.u.type = SelectionNotify;
|
||||||
event.u.selectionNotify.time = stuff->time;
|
event.u.selectionNotify.time = param.time;
|
||||||
event.u.selectionNotify.requestor = stuff->requestor;
|
event.u.selectionNotify.requestor = param.requestor;
|
||||||
event.u.selectionNotify.selection = stuff->selection;
|
event.u.selectionNotify.selection = param.selection;
|
||||||
event.u.selectionNotify.target = stuff->target;
|
event.u.selectionNotify.target = param.target;
|
||||||
event.u.selectionNotify.property = None;
|
event.u.selectionNotify.property = param.property;
|
||||||
WriteEventsToClient(client, 1, &event);
|
WriteEventsToClient(client, 1, &event);
|
||||||
return Success;
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,10 +34,32 @@ typedef struct {
|
||||||
SelectionCallbackKind kind;
|
SelectionCallbackKind kind;
|
||||||
} SelectionInfoRec;
|
} 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 Selection *CurrentSelections;
|
||||||
|
|
||||||
extern CallbackListPtr SelectionCallback;
|
extern CallbackListPtr SelectionCallback;
|
||||||
|
extern CallbackListPtr SelectionFilterCallback;
|
||||||
|
|
||||||
int dixLookupSelection(Selection **result,
|
int dixLookupSelection(Selection **result,
|
||||||
Atom name,
|
Atom name,
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <dix-config.h>
|
#include <dix-config.h>
|
||||||
|
|
||||||
#include "dix/dix_priv.h"
|
#include "dix/dix_priv.h"
|
||||||
|
#include "dix/selection_priv.h"
|
||||||
|
|
||||||
#include "xfixesint.h"
|
#include "xfixesint.h"
|
||||||
#include "xace.h"
|
#include "xace.h"
|
||||||
|
@ -78,13 +79,25 @@ XFixesSelectionCallback(CallbackListPtr *callbacks, void *data, void *args)
|
||||||
UpdateCurrentTimeIf();
|
UpdateCurrentTimeIf();
|
||||||
for (e = selectionEvents; e; e = e->next) {
|
for (e = selectionEvents; e; e = e->next) {
|
||||||
if (e->selection == selection && (e->eventMask & eventMask)) {
|
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 = {
|
xXFixesSelectionNotifyEvent ev = {
|
||||||
.type = XFixesEventBase + XFixesSelectionNotify,
|
.type = XFixesEventBase + XFixesSelectionNotify,
|
||||||
.subtype = subtype,
|
.subtype = subtype,
|
||||||
.window = e->pWindow->drawable.id,
|
.window = e->pWindow->drawable.id,
|
||||||
.owner = (subtype == XFixesSetSelectionOwnerNotify) ?
|
.owner = param.owner,
|
||||||
selection->window : 0,
|
.selection = param.selection,
|
||||||
.selection = e->selection->selection,
|
|
||||||
.timestamp = currentTime.milliseconds,
|
.timestamp = currentTime.milliseconds,
|
||||||
.selectionTimestamp = selection->lastTimeChanged.milliseconds
|
.selectionTimestamp = selection->lastTimeChanged.milliseconds
|
||||||
};
|
};
|
||||||
|
@ -122,8 +135,22 @@ ProcXFixesSelectSelectionInput(ClientPtr client)
|
||||||
REQUEST(xXFixesSelectSelectionInputReq);
|
REQUEST(xXFixesSelectSelectionInputReq);
|
||||||
REQUEST_SIZE_MATCH(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;
|
WindowPtr pWindow;
|
||||||
int rc = dixLookupWindow(&pWindow, stuff->window, client, DixGetAttrAccess);
|
int rc = dixLookupWindow(&pWindow, param.owner, param.client, DixGetAttrAccess);
|
||||||
if (rc != Success)
|
if (rc != Success)
|
||||||
return rc;
|
return rc;
|
||||||
if (stuff->eventMask & ~SelectionAllEvents) {
|
if (stuff->eventMask & ~SelectionAllEvents) {
|
||||||
|
@ -135,13 +162,13 @@ ProcXFixesSelectSelectionInput(ClientPtr client)
|
||||||
SelectionEventPtr *prev, e;
|
SelectionEventPtr *prev, e;
|
||||||
Selection *selection;
|
Selection *selection;
|
||||||
|
|
||||||
rc = dixLookupSelection(&selection, stuff->selection, client, DixGetAttrAccess);
|
rc = dixLookupSelection(&selection, param.selection, param.client, DixGetAttrAccess);
|
||||||
if (rc != Success)
|
if (rc != Success)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
for (prev = &selectionEvents; (e = *prev); prev = &e->next) {
|
for (prev = &selectionEvents; (e = *prev); prev = &e->next) {
|
||||||
if (e->selection == selection &&
|
if (e->selection == selection &&
|
||||||
e->pClient == client && e->pWindow == pWindow) {
|
e->pClient == param.client && e->pWindow == pWindow) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,9 +185,9 @@ ProcXFixesSelectSelectionInput(ClientPtr client)
|
||||||
|
|
||||||
e->next = 0;
|
e->next = 0;
|
||||||
e->selection = selection;
|
e->selection = selection;
|
||||||
e->pClient = client;
|
e->pClient = param.client;
|
||||||
e->pWindow = pWindow;
|
e->pWindow = pWindow;
|
||||||
e->clientResource = FakeClientID(client->index);
|
e->clientResource = FakeClientID(param.client->index);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a resource hanging from the window to
|
* Add a resource hanging from the window to
|
||||||
|
|
Loading…
Reference in New Issue