/* SPDX-License-Identifier: MIT */ /* * Copyright © 2023 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_setup WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include "config.h" #include #include #include "util-bits.h" #include "util-macros.h" #include "util-mem.h" #include "util-io.h" #include "util-strings.h" #include "util-version.h" #include "libeis-private.h" #include "eis-proto.h" static void eis_connection_setup_destroy(struct eis_connection_setup *setup) { struct eis_client * client = eis_connection_setup_get_client(setup); eis_client_unregister_object(client, &setup->proto_object); free(setup->name); } OBJECT_IMPLEMENT_REF(eis_connection_setup); OBJECT_IMPLEMENT_UNREF_CLEANUP(eis_connection_setup); OBJECT_IMPLEMENT_GETTER(eis_connection_setup, version, uint32_t); OBJECT_IMPLEMENT_GETTER_AS_REF(eis_connection_setup, proto_object, const struct brei_object *); static OBJECT_IMPLEMENT_CREATE(eis_connection_setup); static OBJECT_IMPLEMENT_PARENT(eis_connection_setup, eis_client); struct eis_client* eis_connection_setup_get_client(struct eis_connection_setup *setup) { return eis_connection_setup_parent(setup); } struct eis* eis_connection_setup_get_context(struct eis_connection_setup *setup) { struct eis_client *client = eis_connection_setup_parent(setup); return eis_client_get_context(client); } uint32_t eis_connection_setup_get_id(struct eis_connection_setup *setup) { return setup->proto_object.id; } static int client_msg_done(struct eis_connection_setup *setup) { struct eis_client *client = eis_connection_setup_get_client(setup); int rc = -EPROTO; if (setup->client_versions.ei_connection != 0) { eis_client_setup_done(client, setup->name, setup->is_sender, &setup->client_versions); rc = 0; } client->connection = eis_connection_new(client); eis_connection_setup_event_connection(setup, eis_connection_get_id(client->connection), eis_connection_get_version(client->connection)); eis_connection_setup_unref(setup); return rc; } static int client_msg_name(struct eis_connection_setup *setup, const char *name) { if (setup->name) return -EPROTO; setup->name = xstrdup(name); return 0; } static int client_msg_context_type(struct eis_connection_setup *setup, uint32_t type) { switch(type) { case EIS_CONNECTION_SETUP_CONTEXT_TYPE_SENDER: case EIS_CONNECTION_SETUP_CONTEXT_TYPE_RECEIVER: setup->is_sender = !!type; return 0; } return -EPROTO; } static int client_msg_interface_version(struct eis_connection_setup *setup, const char *name, uint32_t version) { struct eis_client *client = eis_connection_setup_get_client(setup); struct eis *eis = eis_client_get_context(client); struct v { const char *name; uint32_t* client_version; uint32_t* server_version; } version_map[] = { #define VERSION_ENTRY(name_) { \ .name = #name_, \ .client_version = &setup->client_versions.name_, \ .server_version = &setup->server_versions.name_, \ } VERSION_ENTRY(ei_callback), VERSION_ENTRY(ei_connection), VERSION_ENTRY(ei_connection_setup), VERSION_ENTRY(ei_seat), VERSION_ENTRY(ei_device), VERSION_ENTRY(ei_pointer), VERSION_ENTRY(ei_keyboard), VERSION_ENTRY(ei_touchscreen), #undef VERSION_ENTRY }; log_debug(eis, "client %#x supports %s version %u", client->id, name, version); if (version == 0) return -EPROTO; struct v *v; ARRAY_FOR_EACH(version_map, v) { if (streq(v->name, name)) { /* Versions must not be set twice */ if (*v->client_version != 0) return -EPROTO; *v->client_version = min(*v->server_version, version); return 0; } } /* Unknown interfaces are ignored */ return 0; } static const struct eis_connection_setup_interface interface = { .done = client_msg_done, .context_type = client_msg_context_type, .name = client_msg_name, .interface_version = client_msg_interface_version, }; const struct eis_connection_setup_interface * eis_connection_setup_get_interface(struct eis_connection_setup *setup) { return &interface; } struct eis_connection_setup * eis_connection_setup_new(struct eis_client *client, const struct eis_client_interface_versions *versions) { struct eis_connection_setup *setup = eis_connection_setup_create(&client->object); setup->proto_object.id = 0; setup->proto_object.implementation = setup; setup->proto_object.interface = &eis_connection_setup_proto_interface; /* This object is always v1 until the client tells us otherwise */ setup->proto_object.version = VERSION_V(1); list_init(&setup->proto_object.link); setup->version = VERSION_V(1); /* our ei-connection-setup version */ setup->server_versions = *versions; eis_client_register_object(client, &setup->proto_object); eis_connection_setup_event_version(setup, versions->ei_connection_setup); return setup; /* ref owned by caller */ }