dix: hook for intercepting window property calls

This hook allows extensions to intercept client requests for changing
window attributes. It can either change the parameters or skip the
entire call (eg. handle all itself) so just the hook provided result
code is returned to the client.

Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
This commit is contained in:
Enrico Weigelt, metux IT consult 2025-03-14 13:40:44 +01:00
parent 43dd9e5f43
commit 8d89be2964
2 changed files with 135 additions and 54 deletions

View File

@ -152,6 +152,7 @@ no_panoramix:
} }
CallbackListPtr PropertyStateCallback; CallbackListPtr PropertyStateCallback;
CallbackListPtr PropertyFilterCallback;
static void static void
deliverPropertyNotifyEvent(WindowPtr pWin, int state, PropertyPtr pProp) deliverPropertyNotifyEvent(WindowPtr pWin, int state, PropertyPtr pProp)
@ -178,58 +179,71 @@ deliverPropertyNotifyEvent(WindowPtr pWin, int state, PropertyPtr pProp)
int int
ProcRotateProperties(ClientPtr client) ProcRotateProperties(ClientPtr client)
{ {
int i, j, delta, rc; int delta, rc;
REQUEST(xRotatePropertiesReq); REQUEST(xRotatePropertiesReq);
WindowPtr pWin;
Atom *atoms;
PropertyPtr *props; /* array of pointer */ PropertyPtr *props; /* array of pointer */
PropertyPtr pProp, saved; PropertyPtr pProp, saved;
REQUEST_FIXED_SIZE(xRotatePropertiesReq, stuff->nAtoms << 2); REQUEST_FIXED_SIZE(xRotatePropertiesReq, stuff->nAtoms << 2);
UpdateCurrentTime(); UpdateCurrentTime();
rc = dixLookupWindow(&pWin, stuff->window, client, DixSetPropAccess);
PropertyFilterParam p = {
.client = client,
.window = stuff->window,
.access_mode = DixWriteAccess,
.atoms = (Atom *) &stuff[1],
.nAtoms = stuff->nAtoms,
.nPositions = stuff->nPositions,
};
CallCallbacks(&PropertyFilterCallback, &p);
if (p.skip)
return p.status;
WindowPtr pWin;
rc = dixLookupWindow(&pWin, p.window, p.client, DixSetPropAccess);
if (rc != Success || stuff->nAtoms <= 0) if (rc != Success || stuff->nAtoms <= 0)
return rc; return rc;
atoms = (Atom *) &stuff[1]; props = calloc(p.nAtoms, sizeof(PropertyPtr));
props = calloc(stuff->nAtoms, sizeof(PropertyPtr)); saved = calloc(p.nAtoms, sizeof(PropertyRec));
saved = calloc(stuff->nAtoms, sizeof(PropertyRec));
if (!props || !saved) { if (!props || !saved) {
rc = BadAlloc; rc = BadAlloc;
goto out; goto out;
} }
for (i = 0; i < stuff->nAtoms; i++) { for (int i = 0; i < p.nAtoms; i++) {
if (!ValidAtom(atoms[i])) { if (!ValidAtom(p.atoms[i])) {
rc = BadAtom; rc = BadAtom;
client->errorValue = atoms[i]; client->errorValue = p.atoms[i];
goto out; goto out;
} }
for (j = i + 1; j < stuff->nAtoms; j++) for (int j = i + 1; j < p.nAtoms; j++)
if (atoms[j] == atoms[i]) { if (p.atoms[j] == p.atoms[i]) {
rc = BadMatch; rc = BadMatch;
goto out; goto out;
} }
rc = dixLookupProperty(&pProp, pWin, atoms[i], client, rc = dixLookupProperty(&pProp, pWin, p.atoms[i], p.client,
DixReadAccess | DixWriteAccess); DixReadAccess | DixWriteAccess);
if (rc != Success) if (rc != Success)
goto out; goto out;
props[i] = pProp; props[i] = pProp;
saved[i] = *pProp; saved[i] = *pProp;
} }
delta = stuff->nPositions; delta = p.nPositions;
/* If the rotation is a complete 360 degrees, then moving the properties /* If the rotation is a complete 360 degrees, then moving the properties
around and generating PropertyNotify events should be skipped. */ around and generating PropertyNotify events should be skipped. */
if (abs(delta) % stuff->nAtoms) { if (abs(delta) % p.nAtoms) {
while (delta < 0) /* faster if abs value is small */ while (delta < 0) /* faster if abs value is small */
delta += stuff->nAtoms; delta += p.nAtoms;
for (i = 0; i < stuff->nAtoms; i++) { for (int i = 0; i < p.nAtoms; i++) {
j = (i + delta) % stuff->nAtoms; int j = (i + delta) % p.nAtoms;
deliverPropertyNotifyEvent(pWin, PropertyNewValue, props[i]); deliverPropertyNotifyEvent(pWin, PropertyNewValue, props[i]);
notifyVRRMode(client, pWin, PropertyNewValue, props[i]); notifyVRRMode(client, pWin, PropertyNewValue, props[i]);
@ -249,7 +263,6 @@ ProcRotateProperties(ClientPtr client)
int int
ProcChangeProperty(ClientPtr client) ProcChangeProperty(ClientPtr client)
{ {
WindowPtr pWin;
char format, mode; char format, mode;
unsigned long len; unsigned long len;
int sizeInBytes, err; int sizeInBytes, err;
@ -277,9 +290,6 @@ ProcChangeProperty(ClientPtr client)
totalSize = len * sizeInBytes; totalSize = len * sizeInBytes;
REQUEST_FIXED_SIZE(xChangePropertyReq, totalSize); REQUEST_FIXED_SIZE(xChangePropertyReq, totalSize);
err = dixLookupWindow(&pWin, stuff->window, client, DixSetPropAccess);
if (err != Success)
return err;
if (!ValidAtom(stuff->property)) { if (!ValidAtom(stuff->property)) {
client->errorValue = stuff->property; client->errorValue = stuff->property;
return BadAtom; return BadAtom;
@ -289,13 +299,30 @@ ProcChangeProperty(ClientPtr client)
return BadAtom; return BadAtom;
} }
err = dixChangeWindowProperty(client, pWin, stuff->property, stuff->type, PropertyFilterParam p = {
(int) format, (int) mode, len, &stuff[1], .client = client,
TRUE); .window = stuff->window,
.property = stuff->property,
.type = stuff->type,
.format = format,
.mode = mode,
.len = len,
.value = &stuff[1],
.sendevent = TRUE,
.access_mode = DixWriteAccess,
};
CallCallbacks(&PropertyFilterCallback, &p);
if (p.skip)
return p.status;
WindowPtr pWin;
err = dixLookupWindow(&pWin, p.window, p.client, DixSetPropAccess);
if (err != Success) if (err != Success)
return err; return err;
else
return Success; return dixChangeWindowProperty(p.client, pWin, p.property, p.type, p.format,
p.mode, p.len, p.value, p.sendevent);
} }
int int
@ -482,20 +509,10 @@ ProcGetProperty(ClientPtr client)
PropertyPtr pProp, prevProp; PropertyPtr pProp, prevProp;
unsigned long n, len, ind; unsigned long n, len, ind;
int rc; int rc;
WindowPtr pWin;
Mask win_mode = DixGetPropAccess, prop_mode = DixReadAccess; Mask win_mode = DixGetPropAccess, prop_mode = DixReadAccess;
REQUEST(xGetPropertyReq); REQUEST(xGetPropertyReq);
REQUEST_SIZE_MATCH(xGetPropertyReq); REQUEST_SIZE_MATCH(xGetPropertyReq);
if (stuff->delete) {
UpdateCurrentTime();
win_mode |= DixSetPropAccess;
prop_mode |= DixDestroyAccess;
}
rc = dixLookupWindow(&pWin, stuff->window, client, win_mode);
if (rc != Success)
return rc;
if (!ValidAtom(stuff->property)) { if (!ValidAtom(stuff->property)) {
client->errorValue = stuff->property; client->errorValue = stuff->property;
@ -510,7 +527,33 @@ ProcGetProperty(ClientPtr client)
return BadAtom; return BadAtom;
} }
rc = dixLookupProperty(&pProp, pWin, stuff->property, client, prop_mode); PropertyFilterParam p = {
.client = client,
.window = stuff->window,
.property = stuff->property,
.type = stuff->type,
.delete = stuff->delete,
.access_mode = prop_mode,
.longOffset = stuff->longOffset,
.longLength = stuff->longLength,
};
CallCallbacks(&PropertyFilterCallback, &p);
if (p.skip)
return p.status;
if (p.delete) {
UpdateCurrentTime();
win_mode |= DixSetPropAccess;
prop_mode |= DixDestroyAccess;
}
WindowPtr pWin;
rc = dixLookupWindow(&pWin, p.window, p.client, win_mode);
if (rc != Success)
return rc;
rc = dixLookupProperty(&pProp, pWin, p.property, p.client, prop_mode);
if (rc == BadMatch) { if (rc == BadMatch) {
xGetPropertyReply rep = { xGetPropertyReply rep = {
.type = X_Reply, .type = X_Reply,
@ -528,8 +571,7 @@ ProcGetProperty(ClientPtr client)
/* If the request type and actual type don't match. Return the /* If the request type and actual type don't match. Return the
property information, but not the data. */ property information, but not the data. */
if (((stuff->type != pProp->type) && (stuff->type != AnyPropertyType)) if (((p.type != pProp->type) && (p.type != AnyPropertyType))) {
) {
xGetPropertyReply rep = { xGetPropertyReply rep = {
.type = X_Reply, .type = X_Reply,
.sequenceNumber = client->sequence, .sequenceNumber = client->sequence,
@ -550,17 +592,17 @@ ProcGetProperty(ClientPtr client)
* Return type, format, value to client * Return type, format, value to client
*/ */
n = (pProp->format / 8) * pProp->size; /* size (bytes) of prop */ n = (pProp->format / 8) * pProp->size; /* size (bytes) of prop */
ind = stuff->longOffset << 2; ind = p.longOffset << 2;
/* If longOffset is invalid such that it causes "len" to /* If longOffset is invalid such that it causes "len" to
be negative, it's a value error. */ be negative, it's a value error. */
if (n < ind) { if (n < ind) {
client->errorValue = stuff->longOffset; client->errorValue = p.longOffset;
return BadValue; return BadValue;
} }
len = min(n - ind, 4 * stuff->longLength); len = min(n - ind, 4 * p.longLength);
xGetPropertyReply rep = { xGetPropertyReply rep = {
.type = X_Reply, .type = X_Reply,
@ -572,8 +614,7 @@ ProcGetProperty(ClientPtr client)
.propertyType = pProp->type .propertyType = pProp->type
}; };
if (stuff->delete && (rep.bytesAfter == 0)) if (p.delete && (rep.bytesAfter == 0)) {
{
deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp); deliverPropertyNotifyEvent(pWin, PropertyDelete, pProp);
notifyVRRMode(client, pWin, PropertyDelete, pProp); notifyVRRMode(client, pWin, PropertyDelete, pProp);
} }
@ -583,7 +624,7 @@ ProcGetProperty(ClientPtr client)
return BadAlloc; return BadAlloc;
memcpy(payload, (char*)(pProp->data) + ind, len); memcpy(payload, (char*)(pProp->data) + ind, len);
if (stuff->delete && (rep.bytesAfter == 0)) { if (p.delete && (rep.bytesAfter == 0)) {
/* Delete the Property */ /* Delete the Property */
if (pWin->properties == pProp) { if (pWin->properties == pProp) {
/* Takes care of head */ /* Takes care of head */
@ -679,20 +720,30 @@ ProcListProperties(ClientPtr client)
int int
ProcDeleteProperty(ClientPtr client) ProcDeleteProperty(ClientPtr client)
{ {
WindowPtr pWin;
REQUEST(xDeletePropertyReq); REQUEST(xDeletePropertyReq);
int result;
REQUEST_SIZE_MATCH(xDeletePropertyReq); REQUEST_SIZE_MATCH(xDeletePropertyReq);
UpdateCurrentTime(); UpdateCurrentTime();
result = dixLookupWindow(&pWin, stuff->window, client, DixSetPropAccess);
if (result != Success)
return result;
if (!ValidAtom(stuff->property)) { if (!ValidAtom(stuff->property)) {
client->errorValue = stuff->property; client->errorValue = stuff->property;
return BadAtom; return BadAtom;
} }
return DeleteProperty(client, pWin, stuff->property); PropertyFilterParam p = {
.client = client,
.window = stuff->window,
.property = stuff->property,
.access_mode = DixRemoveAccess,
};
CallCallbacks(&PropertyFilterCallback, &p);
if (p.skip)
return p.status;
WindowPtr pWin;
int result = dixLookupWindow(&pWin, p.window, p.client, DixSetPropAccess);
if (result != Success)
return result;
return DeleteProperty(p.client, pWin, p.property);
} }

View File

@ -63,7 +63,37 @@ typedef struct _PropertyStateRec {
int state; int state;
} PropertyStateRec; } PropertyStateRec;
typedef struct _PropertyFilterParam {
// used by all requests
ClientPtr client;
Window window;
Atom property;
Atom type;
// in case of RotateProperties
Atom *atoms;
size_t nAtoms;
size_t nPositions;
// caller notification
Bool skip; // TRUE if the call shouldn't be executed
int status; // the status code to return when skip = TRUE
Mask access_mode;
int format;
int mode;
unsigned long len;
const void *value;
Bool sendevent;
// only for GetProperty
BOOL delete;
CARD32 longOffset;
CARD32 longLength;
} PropertyFilterParam;
extern CallbackListPtr PropertyStateCallback; extern CallbackListPtr PropertyStateCallback;
extern CallbackListPtr PropertyFilterCallback;
int dixLookupProperty(PropertyPtr *result, WindowPtr pWin, Atom proprty, int dixLookupProperty(PropertyPtr *result, WindowPtr pWin, Atom proprty,
ClientPtr pClient, Mask access_mode); ClientPtr pClient, Mask access_mode);