mutter/src/backends/meta-eis.c
Peter Hutterer a20aa28af0 backends/eis: Add EIS client support
This adds support for EIS clients in the form of MetaEis and
MetaEisClient.

The purpose is to allow clients to connect and send emulated input events
using EIS.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2628>
2023-07-14 22:23:45 +00:00

299 lines
7.0 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2021 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*/
#include "config.h"
#include <libeis.h>
#include "backends/meta-eis.h"
#include "backends/meta-eis-client.h"
#include "clutter/clutter-mutter.h"
typedef struct _MetaEventSource MetaEventSource;
struct _MetaEventSource
{
GSource source;
MetaEis *meta_eis;
GPollFD event_poll_fd;
};
struct _MetaEis
{
GObject parent_instance;
MetaBackend *backend;
struct eis *eis;
MetaEventSource *event_source;
GHashTable *eis_clients; /* eis_client => MetaEisClient */
};
G_DEFINE_TYPE (MetaEis, meta_eis, G_TYPE_OBJECT)
MetaBackend *
meta_eis_get_backend (MetaEis *meta_eis)
{
return meta_eis->backend;
}
void
meta_eis_remove_all_clients (MetaEis *meta_eis)
{
g_hash_table_remove_all (meta_eis->eis_clients);
}
static void
meta_eis_remove_client (MetaEis *meta_eis,
struct eis_client *eis_client)
{
g_hash_table_remove (meta_eis->eis_clients, eis_client);
}
static void
meta_eis_add_client (MetaEis *meta_eis,
struct eis_client *eis_client)
{
MetaEisClient *meta_eis_client;
meta_eis_client = meta_eis_client_new (meta_eis, eis_client);
g_hash_table_insert (meta_eis->eis_clients,
eis_client_ref (eis_client),
meta_eis_client);
}
static void
process_event (MetaEis *meta_eis,
struct eis_event *event)
{
enum eis_event_type type = eis_event_get_type (event);
struct eis_client *eis_client = eis_event_get_client (event);
MetaEisClient *meta_eis_client;
switch (type)
{
case EIS_EVENT_CLIENT_CONNECT:
meta_eis_add_client (meta_eis, eis_client);
break;
case EIS_EVENT_CLIENT_DISCONNECT:
meta_eis_remove_client (meta_eis, eis_client);
break;
default:
meta_eis_client = g_hash_table_lookup (meta_eis->eis_clients, eis_client);
if (!meta_eis_client)
{
g_warning ("Event for unknown EIS client: %s",
eis_client_get_name (eis_client));
return;
}
meta_eis_client_process_event (meta_eis_client, event);
break;
}
}
static void
process_events (MetaEis *meta_eis)
{
struct eis_event *e;
while ((e = eis_get_event (meta_eis->eis)))
{
process_event (meta_eis, e);
eis_event_unref (e);
}
}
static MetaEventSource *
meta_event_source_new (MetaEis *meta_eis,
int fd,
GSourceFuncs *event_funcs)
{
GSource *source;
MetaEventSource *event_source;
source = g_source_new (event_funcs, sizeof (MetaEventSource));
event_source = (MetaEventSource *) source;
/* setup the source */
event_source->meta_eis = meta_eis;
event_source->event_poll_fd.fd = fd;
event_source->event_poll_fd.events = G_IO_IN;
/* and finally configure and attach the GSource */
g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS);
g_source_add_poll (source, &event_source->event_poll_fd);
g_source_set_can_recurse (source, TRUE);
g_source_attach (source, NULL);
return event_source;
}
static void
meta_event_source_free (MetaEventSource *source)
{
GSource *g_source = (GSource *) source;
/* ignore the return value of close, it's not like we can do something
* about it */
close (source->event_poll_fd.fd);
g_source_destroy (g_source);
g_source_unref (g_source);
}
static gboolean
meta_event_prepare (GSource *g_source,
int *timeout_ms)
{
MetaEventSource *source = (MetaEventSource *) g_source;
MetaEis *meta_eis = source->meta_eis;
struct eis_event *e;
*timeout_ms = -1;
e = eis_peek_event (meta_eis->eis);
if (e)
{
eis_event_unref (e);
return TRUE;
}
return FALSE;
}
static gboolean
meta_event_check (GSource *source)
{
MetaEventSource *event_source = (MetaEventSource *) source;
gboolean retval;
retval = !!(event_source->event_poll_fd.revents & G_IO_IN);
return retval;
}
static gboolean
meta_event_dispatch (GSource *g_source,
GSourceFunc callback,
gpointer user_data)
{
MetaEventSource *source = (MetaEventSource *) g_source;
MetaEis *meta_eis = source->meta_eis;
eis_dispatch (meta_eis->eis);
process_events (meta_eis);
return TRUE;
}
static GSourceFuncs eis_event_funcs = {
meta_event_prepare,
meta_event_check,
meta_event_dispatch,
NULL
};
static int
try_and_find_free_eis_socket (MetaEis *meta_eis)
{
int rc;
int n;
char socketname[16];
for (n = 0; n < 100; n++)
{
g_snprintf (socketname, sizeof (socketname), "eis-%d", n);
rc = eis_setup_backend_socket (meta_eis->eis, socketname);
if (rc == 0)
{
g_info ("Using EIS socket: %s", socketname);
return 0;
}
}
return rc;
}
MetaEis *
meta_eis_new (MetaBackend *backend)
{
MetaEis *meta_eis;
int fd;
int rc;
meta_eis = g_object_new (META_TYPE_EIS, NULL);
meta_eis->backend = backend;
meta_eis->eis = eis_new (meta_eis);
rc = try_and_find_free_eis_socket (meta_eis);
if (rc != 0)
{
g_warning ("Failed to initialize the EIS socket: %s", g_strerror (-rc));
g_clear_pointer (&meta_eis->eis, eis_unref);
return NULL;
}
fd = eis_get_fd (meta_eis->eis);
meta_eis->event_source = meta_event_source_new (meta_eis, fd, &eis_event_funcs);
return meta_eis;
}
static void
meta_eis_init (MetaEis *meta_eis)
{
}
static void
meta_eis_constructed (GObject *object)
{
MetaEis *meta_eis = META_EIS (object);
meta_eis->eis_clients = g_hash_table_new_full (g_direct_hash, g_direct_equal,
(GDestroyNotify) eis_client_unref,
(GDestroyNotify) g_object_unref);
if (G_OBJECT_CLASS (meta_eis_parent_class)->constructed)
G_OBJECT_CLASS (meta_eis_parent_class)->constructed (object);
}
static void
meta_eis_finalize (GObject *object)
{
MetaEis *meta_eis = META_EIS (object);
g_clear_pointer (&meta_eis->event_source, meta_event_source_free);
g_clear_pointer (&meta_eis->eis, eis_unref);
g_clear_pointer (&meta_eis->eis_clients, g_hash_table_destroy);
}
static void
meta_eis_class_init (MetaEisClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = meta_eis_constructed;
object_class->finalize = meta_eis_finalize;
}