/* * Copyright © 2009 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: Peter Hutterer * */ /** * @file Protocol handling for the XIQueryDevice request/reply. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include "inputstr.h" #include #include #include #include "xkbstr.h" #include "xkbsrv.h" #include "xserver-properties.h" #include "exevents.h" #include "xace.h" #include "xiquerydevice.h" static Bool ShouldSkipDevice(ClientPtr client, int deviceid, DeviceIntPtr d); static int ListDeviceInfo(ClientPtr client, DeviceIntPtr dev, xXIDeviceInfo* info); static int SizeDeviceInfo(DeviceIntPtr dev); static void SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo* info); int SProcXIQueryDevice(ClientPtr client) { char n; REQUEST(xXIQueryDeviceReq); swaps(&stuff->length, n); swaps(&stuff->deviceid, n); return ProcXIQueryDevice(client); } int ProcXIQueryDevice(ClientPtr client) { xXIQueryDeviceReply rep; DeviceIntPtr dev = NULL; int rc = Success; int i = 0, len = 0; char *info, *ptr; Bool *skip = NULL; REQUEST(xXIQueryDeviceReq); REQUEST_SIZE_MATCH(xXIQueryDeviceReq); if (stuff->deviceid != XIAllDevices && stuff->deviceid != XIAllMasterDevices) { rc = dixLookupDevice(&dev, stuff->deviceid, client, DixGetAttrAccess); if (rc != Success) { client->errorValue = stuff->deviceid; return rc; } len += SizeDeviceInfo(dev); } else { skip = calloc(sizeof(Bool), inputInfo.numDevices); if (!skip) return BadAlloc; for (dev = inputInfo.devices; dev; dev = dev->next, i++) { skip[i] = ShouldSkipDevice(client, stuff->deviceid, dev); if (!skip[i]) len += SizeDeviceInfo(dev); } for (dev = inputInfo.off_devices; dev; dev = dev->next, i++) { skip[i] = ShouldSkipDevice(client, stuff->deviceid, dev); if (!skip[i]) len += SizeDeviceInfo(dev); } } info = calloc(1, len); if (!info) return BadAlloc; memset(&rep, 0, sizeof(xXIQueryDeviceReply)); rep.repType = X_Reply; rep.RepType = X_XIQueryDevice; rep.sequenceNumber = client->sequence; rep.length = len/4; rep.num_devices = 0; ptr = info; if (dev) { len = ListDeviceInfo(client, dev, (xXIDeviceInfo*)info); if (client->swapped) SwapDeviceInfo(dev, (xXIDeviceInfo*)info); info += len; rep.num_devices = 1; } else { i = 0; for (dev = inputInfo.devices; dev; dev = dev->next, i++) { if (!skip[i]) { len = ListDeviceInfo(client, dev, (xXIDeviceInfo*)info); if (client->swapped) SwapDeviceInfo(dev, (xXIDeviceInfo*)info); info += len; rep.num_devices++; } } for (dev = inputInfo.off_devices; dev; dev = dev->next, i++) { if (!skip[i]) { len = ListDeviceInfo(client, dev, (xXIDeviceInfo*)info); if (client->swapped) SwapDeviceInfo(dev, (xXIDeviceInfo*)info); info += len; rep.num_devices++; } } } len = rep.length*4; WriteReplyToClient(client, sizeof(xXIQueryDeviceReply), &rep); WriteToClient(client, len, ptr); free(ptr); free(skip); return rc; } void SRepXIQueryDevice(ClientPtr client, int size, xXIQueryDeviceReply *rep) { char n; swaps(&rep->sequenceNumber, n); swapl(&rep->length, n); swaps(&rep->num_devices, n); /* Device info is already swapped, see ProcXIQueryDevice */ WriteToClient(client, size, (char *)rep); } /** * @return Whether the device should be included in the returned list. */ static Bool ShouldSkipDevice(ClientPtr client, int deviceid, DeviceIntPtr dev) { /* if all devices are not being queried, only master devices are */ if (deviceid == XIAllDevices || IsMaster(dev)) { int rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixGetAttrAccess); if (rc == Success) return FALSE; } return TRUE; } /** * @return The number of bytes needed to store this device's xXIDeviceInfo * (and its classes). */ static int SizeDeviceInfo(DeviceIntPtr dev) { int len = sizeof(xXIDeviceInfo); /* 4-padded name */ len += pad_to_int32(strlen(dev->name)); return len + SizeDeviceClasses(dev); } /* * @return The number of bytes needed to store this device's classes. */ int SizeDeviceClasses(DeviceIntPtr dev) { int len = 0; if (dev->button) { len += sizeof(xXIButtonInfo); len += dev->button->numButtons * sizeof(Atom); len += pad_to_int32(bits_to_bytes(dev->button->numButtons)); } if (dev->key) { XkbDescPtr xkb = dev->key->xkbInfo->desc; len += sizeof(xXIKeyInfo); len += (xkb->max_key_code - xkb->min_key_code + 1) * sizeof(uint32_t); } if (dev->valuator) len += sizeof(xXIValuatorInfo) * dev->valuator->numAxes; return len; } /** * Write button information into info. * @return Number of bytes written into info. */ int ListButtonInfo(DeviceIntPtr dev, xXIButtonInfo* info, Bool reportState) { unsigned char *bits; int mask_len; int i; if (!dev || !dev->button) return 0; mask_len = bytes_to_int32(bits_to_bytes(dev->button->numButtons)); info->type = ButtonClass; info->num_buttons = dev->button->numButtons; info->length = bytes_to_int32(sizeof(xXIButtonInfo)) + info->num_buttons + mask_len; info->sourceid = dev->button->sourceid; bits = (unsigned char*)&info[1]; memset(bits, 0, mask_len * 4); if (reportState) for (i = 0; i < dev->button->numButtons; i++) if (BitIsOn(dev->button->down, i)) SetBit(bits, i); bits += mask_len * 4; memcpy(bits, dev->button->labels, dev->button->numButtons * sizeof(Atom)); return info->length * 4; } static void SwapButtonInfo(DeviceIntPtr dev, xXIButtonInfo* info) { char n; Atom *btn; int i; swaps(&info->type, n); swaps(&info->length, n); swaps(&info->sourceid, n); for (i = 0, btn = (Atom*)&info[1]; i < info->num_buttons; i++, btn++) swaps(btn, n); swaps(&info->num_buttons, n); } /** * Write key information into info. * @return Number of bytes written into info. */ int ListKeyInfo(DeviceIntPtr dev, xXIKeyInfo* info) { int i; XkbDescPtr xkb = dev->key->xkbInfo->desc; uint32_t *kc; info->type = KeyClass; info->num_keycodes = xkb->max_key_code - xkb->min_key_code + 1; info->length = sizeof(xXIKeyInfo)/4 + info->num_keycodes; info->sourceid = dev->key->sourceid; kc = (uint32_t*)&info[1]; for (i = xkb->min_key_code; i <= xkb->max_key_code; i++, kc++) *kc = i; return info->length * 4; } static void SwapKeyInfo(DeviceIntPtr dev, xXIKeyInfo* info) { char n; uint32_t *key; int i; swaps(&info->type, n); swaps(&info->length, n); swaps(&info->sourceid, n); for (i = 0, key = (uint32_t*)&info[1]; i < info->num_keycodes; i++, key++) swapl(key, n); swaps(&info->num_keycodes, n); } /** * List axis information for the given axis. * * @return The number of bytes written into info. */ int ListValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info, int axisnumber, Bool reportState) { ValuatorClassPtr v = dev->valuator; info->type = ValuatorClass; info->length = sizeof(xXIValuatorInfo)/4; info->label = v->axes[axisnumber].label; info->min.integral = v->axes[axisnumber].min_value; info->min.frac = 0; info->max.integral = v->axes[axisnumber].max_value; info->max.frac = 0; info->value.integral = (int)v->axisVal[axisnumber]; info->value.frac = (int)(v->axisVal[axisnumber] * (1 << 16) * (1 << 16)); info->resolution = v->axes[axisnumber].resolution; info->number = axisnumber; info->mode = v->mode; /* Server doesn't have per-axis mode yet */ info->sourceid = v->sourceid; if (!reportState) info->value = info->min; return info->length * 4; } static void SwapValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info) { char n; swaps(&info->type, n); swaps(&info->length, n); swapl(&info->label, n); swapl(&info->min.integral, n); swapl(&info->min.frac, n); swapl(&info->max.integral, n); swapl(&info->max.frac, n); swaps(&info->number, n); swaps(&info->sourceid, n); } int GetDeviceUse(DeviceIntPtr dev, uint16_t *attachment) { DeviceIntPtr master = dev->u.master; int use; if (IsMaster(dev)) { DeviceIntPtr paired = GetPairedDevice(dev); use = IsPointerDevice(dev) ? XIMasterPointer : XIMasterKeyboard; *attachment = (paired ? paired->id : 0); } else if (master) { use = IsPointerDevice(master) ? XISlavePointer : XISlaveKeyboard; *attachment = master->id; } else use = XIFloatingSlave; return use; } /** * Write the info for device dev into the buffer pointed to by info. * * @return The number of bytes used. */ static int ListDeviceInfo(ClientPtr client, DeviceIntPtr dev, xXIDeviceInfo* info) { char *any = (char*)&info[1]; int len = 0, total_len = 0; info->deviceid = dev->id; info->use = GetDeviceUse(dev, &info->attachment); info->num_classes = 0; info->name_len = strlen(dev->name); info->enabled = dev->enabled; total_len = sizeof(xXIDeviceInfo); len = pad_to_int32(info->name_len); memset(any, 0, len); strncpy(any, dev->name, info->name_len); any += len; total_len += len; total_len += ListDeviceClasses(client, dev, any, &info->num_classes); return total_len; } /** * Write the class info of the device into the memory pointed to by any, set * nclasses to the number of classes in total and return the number of bytes * written. */ int ListDeviceClasses(ClientPtr client, DeviceIntPtr dev, char *any, uint16_t *nclasses) { int total_len = 0; int len; int i; int rc; /* Check if the current device state should be suppressed */ rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixReadAccess); if (dev->button) { (*nclasses)++; len = ListButtonInfo(dev, (xXIButtonInfo*)any, rc == Success); any += len; total_len += len; } if (dev->key) { (*nclasses)++; len = ListKeyInfo(dev, (xXIKeyInfo*)any); any += len; total_len += len; } for (i = 0; dev->valuator && i < dev->valuator->numAxes; i++) { (*nclasses)++; len = ListValuatorInfo(dev, (xXIValuatorInfo*)any, i, rc == Success); any += len; total_len += len; } return total_len; } static void SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo* info) { char n; char *any = (char*)&info[1]; int i; /* Skip over name */ any += pad_to_int32(info->name_len); for (i = 0; i < info->num_classes; i++) { int len = ((xXIAnyInfo*)any)->length; switch(((xXIAnyInfo*)any)->type) { case XIButtonClass: SwapButtonInfo(dev, (xXIButtonInfo*)any); break; case XIKeyClass: SwapKeyInfo(dev, (xXIKeyInfo*)any); break; case XIValuatorClass: SwapValuatorInfo(dev, (xXIValuatorInfo*)any); break; } any += len * 4; } swaps(&info->deviceid, n); swaps(&info->use, n); swaps(&info->attachment, n); swaps(&info->num_classes, n); swaps(&info->name_len, n); }