/* * 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 "inpututils.h" #include "exglobals.h" #include "privates.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 _X_COLD SProcXIQueryDevice(ClientPtr client) { REQUEST(xXIQueryDeviceReq); REQUEST_SIZE_MATCH(xXIQueryDeviceReq); swaps(&stuff->length); swaps(&stuff->deviceid); 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) { free(skip); return BadAlloc; } rep = (xXIQueryDeviceReply) { .repType = X_Reply, .RepType = X_XIQueryDevice, .sequenceNumber = client->sequence, .length = len / 4, .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) { swaps(&rep->sequenceNumber); swapl(&rep->length); swaps(&rep->num_devices); /* Device info is already swapped, see ProcXIQueryDevice */ WriteToClient(client, size, 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) { int i; len += (sizeof(xXIValuatorInfo)) * dev->valuator->numAxes; for (i = 0; i < dev->valuator->numAxes; i++) { if (dev->valuator->axes[i].scroll.type != SCROLL_TYPE_NONE) len += sizeof(xXIScrollInfo); } } if (dev->touch) len += sizeof(xXITouchInfo); if (dev->gesture) len += sizeof(xXIGestureInfo); return len; } /** * Get pointers to button information areas holding button mask and labels. */ static void ButtonInfoData(xXIButtonInfo *info, int *mask_words, unsigned char **mask, Atom **atoms) { *mask_words = bytes_to_int32(bits_to_bytes(info->num_buttons)); *mask = (unsigned char*) &info[1]; *atoms = (Atom*) ((*mask) + (*mask_words) * 4); } /** * Write button information into info. * @return Number of bytes written into info. */ int ListButtonInfo(DeviceIntPtr dev, xXIButtonInfo * info, Bool reportState) { unsigned char *bits; Atom *labels; int mask_len; int i; if (!dev || !dev->button) return 0; info->type = ButtonClass; info->num_buttons = dev->button->numButtons; ButtonInfoData(info, &mask_len, &bits, &labels); info->length = bytes_to_int32(sizeof(xXIButtonInfo)) + info->num_buttons + mask_len; info->sourceid = dev->button->sourceid; 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); memcpy(labels, dev->button->labels, dev->button->numButtons * sizeof(Atom)); return info->length * 4; } static void SwapButtonInfo(DeviceIntPtr dev, xXIButtonInfo * info) { Atom *btn; int mask_len; unsigned char *mask; int i; ButtonInfoData(info, &mask_len, &mask, &btn); swaps(&info->type); swaps(&info->length); swaps(&info->sourceid); for (i = 0 ; i < info->num_buttons; i++, btn++) swapl(btn); swaps(&info->num_buttons); } /** * 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) { uint32_t *key; int i; swaps(&info->type); swaps(&info->length); swaps(&info->sourceid); for (i = 0, key = (uint32_t *) &info[1]; i < info->num_keycodes; i++, key++) swapl(key); swaps(&info->num_keycodes); } /** * 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 = double_to_fp3232(v->axisVal[axisnumber]); info->resolution = v->axes[axisnumber].resolution; info->number = axisnumber; info->mode = valuator_get_mode(dev, axisnumber); info->sourceid = v->sourceid; if (!reportState) info->value = info->min; return info->length * 4; } static void SwapValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo * info) { swaps(&info->type); swaps(&info->length); swapl(&info->label); swapl(&info->min.integral); swapl(&info->min.frac); swapl(&info->max.integral); swapl(&info->max.frac); swapl(&info->value.integral); swapl(&info->value.frac); swapl(&info->resolution); swaps(&info->number); swaps(&info->sourceid); } int ListScrollInfo(DeviceIntPtr dev, xXIScrollInfo * info, int axisnumber) { ValuatorClassPtr v = dev->valuator; AxisInfoPtr axis = &v->axes[axisnumber]; if (axis->scroll.type == SCROLL_TYPE_NONE) return 0; info->type = XIScrollClass; info->length = sizeof(xXIScrollInfo) / 4; info->number = axisnumber; switch (axis->scroll.type) { case SCROLL_TYPE_VERTICAL: info->scroll_type = XIScrollTypeVertical; break; case SCROLL_TYPE_HORIZONTAL: info->scroll_type = XIScrollTypeHorizontal; break; default: ErrorF("[Xi] Unknown scroll type %d. This is a bug.\n", axis->scroll.type); break; } info->increment = double_to_fp3232(axis->scroll.increment); info->sourceid = v->sourceid; info->flags = 0; if (axis->scroll.flags & SCROLL_FLAG_DONT_EMULATE) info->flags |= XIScrollFlagNoEmulation; if (axis->scroll.flags & SCROLL_FLAG_PREFERRED) info->flags |= XIScrollFlagPreferred; return info->length * 4; } static void SwapScrollInfo(DeviceIntPtr dev, xXIScrollInfo * info) { swaps(&info->type); swaps(&info->length); swaps(&info->number); swaps(&info->sourceid); swaps(&info->scroll_type); swapl(&info->increment.integral); swapl(&info->increment.frac); } /** * List multitouch information * * @return The number of bytes written into info. */ int ListTouchInfo(DeviceIntPtr dev, xXITouchInfo * touch) { touch->type = XITouchClass; touch->length = sizeof(xXITouchInfo) >> 2; touch->sourceid = dev->touch->sourceid; touch->mode = dev->touch->mode; touch->num_touches = dev->touch->num_touches; return touch->length << 2; } static void SwapTouchInfo(DeviceIntPtr dev, xXITouchInfo * touch) { swaps(&touch->type); swaps(&touch->length); swaps(&touch->sourceid); } static Bool ShouldListGestureInfo(ClientPtr client) { /* libxcb 14.1 and older are not forwards-compatible with new device classes as it does not * properly ignore unknown device classes. Since breaking libxcb would break quite a lot of * applications, we instead report Gesture device class only if the client advertised support * for XI 2.4. Clients may still not work in cases when a client advertises XI 2.4 support * and then a completely separate module within the client uses broken libxcb to call * XIQueryDevice. */ XIClientPtr pXIClient = dixLookupPrivate(&client->devPrivates, XIClientPrivateKey); if (pXIClient->major_version) { return version_compare(pXIClient->major_version, pXIClient->minor_version, 2, 4) >= 0; } return FALSE; } /** * List gesture information * * @return The number of bytes written into info. */ static int ListGestureInfo(DeviceIntPtr dev, xXIGestureInfo * gesture) { gesture->type = XIGestureClass; gesture->length = sizeof(xXIGestureInfo) >> 2; gesture->sourceid = dev->gesture->sourceid; gesture->num_touches = dev->gesture->max_touches; return gesture->length << 2; } static void SwapGestureInfo(DeviceIntPtr dev, xXIGestureInfo * gesture) { swaps(&gesture->type); swaps(&gesture->length); swaps(&gesture->sourceid); } int GetDeviceUse(DeviceIntPtr dev, uint16_t * attachment) { DeviceIntPtr master = GetMaster(dev, MASTER_ATTACHED); int use; if (IsMaster(dev)) { DeviceIntPtr paired = GetPairedDevice(dev); use = IsPointerDevice(dev) ? XIMasterPointer : XIMasterKeyboard; *attachment = (paired ? paired->id : 0); } else if (!IsFloating(dev)) { 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; } for (i = 0; dev->valuator && i < dev->valuator->numAxes; i++) { len = ListScrollInfo(dev, (xXIScrollInfo *) any, i); if (len) (*nclasses)++; any += len; total_len += len; } if (dev->touch) { (*nclasses)++; len = ListTouchInfo(dev, (xXITouchInfo *) any); any += len; total_len += len; } if (dev->gesture && ShouldListGestureInfo(client)) { (*nclasses)++; len = ListGestureInfo(dev, (xXIGestureInfo *) any); any += len; total_len += len; } return total_len; } static void SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo * info) { 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; case XIScrollClass: SwapScrollInfo(dev, (xXIScrollInfo *) any); break; case XITouchClass: SwapTouchInfo(dev, (xXITouchInfo *) any); break; case XIGestureClass: SwapGestureInfo(dev, (xXIGestureInfo *) any); break; } any += len * 4; } swaps(&info->deviceid); swaps(&info->use); swaps(&info->attachment); swaps(&info->num_classes); swaps(&info->name_len); }