mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2026-02-04 23:30:34 +01:00
Previously, a client could only bind to a capability immediately after SEAT_ADDED and with a rather awkward API to confirm the capabilities. Change this to allow for dynamic binding of capabilities, i.e. a client calls ei_bind_capability() or ei_unbind_capability() whenever it feels like, causing the respective devices of this capabilty to be added or removed. This allows for clients that are temporarily disinterested in a capability but may require said capability later. The default function takes one capability, a helper for enable/disable multiple capabilities in one go is provided as well. On the protocol, only the "bind" request exists which always represents the currently wanted set of capabilities. Note that the helper functions for multiple capabilities require NULL (not zero) as sentinel, thanks to gcc.
247 lines
5.8 KiB
C
247 lines
5.8 KiB
C
/* SPDX-License-Identifier: MIT */
|
|
/*
|
|
* Copyright © 2020 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.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "util-bits.h"
|
|
#include "util-macros.h"
|
|
#include "util-mem.h"
|
|
#include "util-io.h"
|
|
#include "util-strings.h"
|
|
|
|
#include "libei-private.h"
|
|
|
|
static void
|
|
ei_seat_destroy(struct ei_seat *seat)
|
|
{
|
|
free(seat->name);
|
|
}
|
|
|
|
_public_
|
|
OBJECT_IMPLEMENT_REF(ei_seat);
|
|
_public_
|
|
OBJECT_IMPLEMENT_UNREF_CLEANUP(ei_seat);
|
|
|
|
static
|
|
OBJECT_IMPLEMENT_CREATE(ei_seat);
|
|
static
|
|
OBJECT_IMPLEMENT_PARENT(ei_seat, ei);
|
|
_public_
|
|
OBJECT_IMPLEMENT_GETTER(ei_seat, name, const char *);
|
|
_public_
|
|
OBJECT_IMPLEMENT_SETTER(ei_seat, user_data, void *);
|
|
|
|
_public_ struct ei*
|
|
ei_seat_get_context(struct ei_seat *seat)
|
|
{
|
|
assert(seat);
|
|
return ei_seat_parent(seat);
|
|
}
|
|
|
|
struct ei_seat *
|
|
ei_seat_new(struct ei *ei, uint32_t id, const char *name, uint32_t capabilities)
|
|
{
|
|
struct ei_seat *seat = ei_seat_create(&ei->object);
|
|
|
|
seat->state = EI_SEAT_STATE_NEW;
|
|
seat->name = xstrdup(name);
|
|
seat->id = id;
|
|
seat->capabilities = capabilities;
|
|
seat->capabilities_bound = 0;
|
|
|
|
list_init(&seat->devices);
|
|
list_init(&seat->devices_removed);
|
|
list_init(&seat->link);
|
|
|
|
return seat; /* ref owned by caller */
|
|
}
|
|
|
|
void
|
|
ei_seat_remove(struct ei_seat *seat)
|
|
{
|
|
/* Sigh, this is terrible and needs to be fixed:
|
|
* if our fd is broken, trying to send any event causes an ei_disconnect(),
|
|
* which eventually calls in here. So we need to guard this function
|
|
* against nested callers. */
|
|
if (seat->state == EI_SEAT_STATE_REMOVED)
|
|
return;
|
|
|
|
struct ei_device *d;
|
|
|
|
/* If the server disconnects us before processing a new device, we
|
|
* need to clean this up in the library */
|
|
list_for_each_safe(d, &seat->devices, link) {
|
|
/* remove the device */
|
|
ei_device_close(d);
|
|
/* And pretend to process the removed message from
|
|
* the server */
|
|
ei_device_removed_by_server(d);
|
|
}
|
|
|
|
/* Check the seat state again, because the above device removal may
|
|
* have triggered ei_disconnect() */
|
|
if (seat->state != EI_SEAT_STATE_REMOVED) {
|
|
seat->state = EI_SEAT_STATE_REMOVED;
|
|
list_remove(&seat->link);
|
|
list_init(&seat->link);
|
|
ei_queue_seat_removed_event(seat);
|
|
ei_seat_unref(seat);
|
|
}
|
|
}
|
|
|
|
struct ei_device *
|
|
ei_seat_find_device(struct ei_seat *seat, uint32_t deviceid)
|
|
{
|
|
struct ei_device *device;
|
|
|
|
list_for_each(device, &seat->devices, link) {
|
|
if (device->id == deviceid)
|
|
return device;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
_public_ bool
|
|
ei_seat_has_capability(struct ei_seat *seat,
|
|
enum ei_device_capability cap)
|
|
{
|
|
switch (cap) {
|
|
case EI_DEVICE_CAP_POINTER:
|
|
case EI_DEVICE_CAP_POINTER_ABSOLUTE:
|
|
case EI_DEVICE_CAP_KEYBOARD:
|
|
case EI_DEVICE_CAP_TOUCH:
|
|
return flag_is_set(seat->capabilities, cap);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
_public_ void
|
|
ei_seat_bind_capability(struct ei_seat *seat, enum ei_device_capability cap)
|
|
{
|
|
ei_seat_bind_capabilities(seat, cap, NULL);
|
|
}
|
|
|
|
static inline bool
|
|
is_known_cap(enum ei_device_capability cap)
|
|
{
|
|
switch (cap) {
|
|
case EI_DEVICE_CAP_POINTER_ABSOLUTE:
|
|
case EI_DEVICE_CAP_POINTER:
|
|
case EI_DEVICE_CAP_KEYBOARD:
|
|
case EI_DEVICE_CAP_TOUCH:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
_public_ void
|
|
ei_seat_bind_capabilities(struct ei_seat *seat, ...)
|
|
{
|
|
switch (seat->state) {
|
|
case EI_SEAT_STATE_NEW:
|
|
break;
|
|
case EI_SEAT_STATE_REMOVED:
|
|
return;
|
|
}
|
|
|
|
uint32_t mask = seat->capabilities_bound;
|
|
enum ei_device_capability cap;
|
|
|
|
va_list args;
|
|
va_start(args, seat);
|
|
while ((cap = va_arg(args, enum ei_device_capability)) > 0) {
|
|
if (!is_known_cap(cap))
|
|
continue;
|
|
|
|
flag_set(mask, cap);
|
|
}
|
|
|
|
mask &= seat->capabilities;
|
|
|
|
if (seat->capabilities_bound == mask)
|
|
return;
|
|
|
|
seat->capabilities_bound = mask;
|
|
ei_send_seat_bind(seat, seat->capabilities_bound);
|
|
}
|
|
|
|
_public_ void
|
|
ei_seat_unbind_capability(struct ei_seat *seat,
|
|
enum ei_device_capability cap)
|
|
{
|
|
switch (seat->state) {
|
|
case EI_SEAT_STATE_NEW:
|
|
break;
|
|
case EI_SEAT_STATE_REMOVED:
|
|
return;
|
|
}
|
|
|
|
if (!flag_is_set(seat->capabilities_bound, cap))
|
|
return;
|
|
|
|
flag_clear(seat->capabilities_bound, cap);
|
|
|
|
struct ei_device *device;
|
|
list_for_each(device, &seat->devices, link) {
|
|
if (ei_device_has_capability(device, cap))
|
|
ei_device_close(device);
|
|
}
|
|
ei_send_seat_bind(seat, seat->capabilities_bound);
|
|
}
|
|
|
|
_public_ void
|
|
ei_seat_unbind_capabilities(struct ei_seat *seat, ...)
|
|
{
|
|
switch (seat->state) {
|
|
case EI_SEAT_STATE_NEW:
|
|
break;
|
|
case EI_SEAT_STATE_REMOVED:
|
|
return;
|
|
}
|
|
|
|
uint32_t mask = 0;
|
|
enum ei_device_capability cap;
|
|
|
|
va_list args;
|
|
va_start(args, seat);
|
|
while ((cap = va_arg(args, enum ei_device_capability)) > 0) {
|
|
if (!is_known_cap(cap))
|
|
continue;
|
|
|
|
flag_set(mask, cap);
|
|
}
|
|
|
|
mask = seat->capabilities & ~mask;
|
|
|
|
if (seat->capabilities_bound == mask)
|
|
return;
|
|
|
|
seat->capabilities_bound = mask;
|
|
ei_send_seat_bind(seat, seat->capabilities_bound);
|
|
}
|