mirror of
https://gitlab.freedesktop.org/libinput/libei.git
synced 2026-01-09 16:50:20 +01:00
Purge the portal code
The original idea here was that we would have an EmulatedInput portal that allows the application to connect directly to the EIS implementation to exchange input events - instead of ping-ponging DBus events through the xdg-desktop-portal as the RemoteDesktop portal requires. This is no longer accurate, there are suggested PRs open to add RemoteDesktop.ConnectToEIS to achieve the same through the existing RemoteDesktop interface [1] and to add a new InputCapture portal to allow for events to be sent to a libei receiver context [2]. The example EmulatedInput portal is thus superfluous and can be removed from here. We could switch the ei_setup_backend_portal() code to use RemoteDesktop or InputCapture, depending on the context type, the utility of this is questionable. Interaction with portals is complex, one needs to implement the Session/Request interfaces correctly and in the case of InputCapture also handle the complex zones/pointer barrier setup. libportal does some of this (or it will, anyway) so it's more useful for an application to use libportal and then just pass the received fd to libei. If there is a future need for this to be handled as part of libei, we can (re)implement this, but for now it's best to just purge all of this. [1] https://github.com/flatpak/xdg-desktop-portal/pull/762 [2] https://github.com/flatpak/xdg-desktop-portal/pull/714
This commit is contained in:
parent
97254391dc
commit
8fc654bfb0
17 changed files with 5 additions and 1453 deletions
|
|
@ -254,14 +254,6 @@ build-no-doxygen@fedora:37:
|
|||
before_script:
|
||||
- dnf remove -y doxygen
|
||||
|
||||
build-no-portal@fedora:37:
|
||||
extends:
|
||||
- .fedora-build@template
|
||||
before_script:
|
||||
- dnf remove -y libsystemd-devel
|
||||
variables:
|
||||
MESON_ARGS: "-Dportal=false"
|
||||
|
||||
valgrind@fedora:37:
|
||||
extends:
|
||||
- .fedora-build@template
|
||||
|
|
|
|||
|
|
@ -275,14 +275,6 @@ build-no-doxygen@{{distro.name}}:{{version}}:
|
|||
before_script:
|
||||
- dnf remove -y doxygen
|
||||
|
||||
build-no-portal@{{distro.name}}:{{version}}:
|
||||
extends:
|
||||
- .{{distro.name}}-build@template
|
||||
before_script:
|
||||
- dnf remove -y libsystemd-devel
|
||||
variables:
|
||||
MESON_ARGS: "-Dportal=false"
|
||||
|
||||
valgrind@{{distro.name}}:{{version}}:
|
||||
extends:
|
||||
- .{{distro.name}}-build@template
|
||||
|
|
|
|||
|
|
@ -58,10 +58,6 @@ implementation and sends events.
|
|||
The `eis-demo-server` is a minimal EIS implementation that accepts all
|
||||
requests and prints them to screen.
|
||||
|
||||
The `eis-fake-portal` is a minimal [XDG Desktop
|
||||
Portal](https://github.com/flatpak/xdg-desktop-portal/) implementation that
|
||||
connects a portal-aware libei client with an EIS implementation.
|
||||
|
||||
@section building_against Building against libei or libeis
|
||||
|
||||
libei and libeis provides
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
function main():
|
||||
ctx = ei_new()
|
||||
ei_portal_connect(ctx)
|
||||
ei_connect(ctx)
|
||||
ei_dispatch(ctx)
|
||||
|
||||
# let's say this is a blocking wait
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
function main():
|
||||
ctx = eis_new()
|
||||
eis_portal_init(ctx)
|
||||
eis_init(ctx)
|
||||
|
||||
while True:
|
||||
poll(eis_get_fd()):
|
||||
|
|
|
|||
25
meson.build
25
meson.build
|
|
@ -94,25 +94,12 @@ src_libei = [
|
|||
'src/libei-proto.h',
|
||||
'src/libei-proto.c',
|
||||
'src/libei-region.c',
|
||||
'src/libei-stubs.c',
|
||||
proto_headers,
|
||||
]
|
||||
|
||||
|
||||
if get_option('portal')
|
||||
dep_systemd = dependency('libsystemd')
|
||||
config_h.set10('ENABLE_LIBEI_PORTAL', true)
|
||||
src_libei += [
|
||||
'src/libei-portal.c',
|
||||
]
|
||||
else
|
||||
dep_systemd = dependency('', required: false)
|
||||
endif
|
||||
|
||||
deps_libei = [
|
||||
dep_libutil,
|
||||
dep_protobuf,
|
||||
dep_systemd,
|
||||
]
|
||||
|
||||
lib_libei = shared_library('ei',
|
||||
|
|
@ -133,7 +120,6 @@ pkgconfig.generate(lib_libei,
|
|||
version: meson.project_version(),
|
||||
libraries: lib_libei,
|
||||
variables: [
|
||||
'portal=' + get_option('portal').to_string(),
|
||||
'protocol_version=' + protocol_version.to_string(),
|
||||
],
|
||||
)
|
||||
|
|
@ -238,17 +224,6 @@ executable('ei-debug-events',
|
|||
dependencies: [dep_libutil, dep_libei, dep_libevdev],
|
||||
install: true)
|
||||
|
||||
if get_option('portal')
|
||||
executable('eis-fake-portal',
|
||||
'tools/eis-fake-portal.c',
|
||||
include_directories: 'src',
|
||||
dependencies: [dep_libutil, dep_systemd, dep_libreis])
|
||||
executable('eis-fake-impl-portal',
|
||||
'tools/eis-fake-impl-portal.c',
|
||||
include_directories: 'src',
|
||||
dependencies: [dep_libutil, dep_systemd, dep_libreis])
|
||||
endif
|
||||
|
||||
# tests
|
||||
if get_option('tests')
|
||||
subproject('munit', default_options: 'werror=false')
|
||||
|
|
|
|||
|
|
@ -1,3 +1,2 @@
|
|||
option('portal', type: 'boolean', value: 'true', description: 'Enable/disable org.freedesktop.portal support')
|
||||
option('documentation', type: 'boolean', value: 'false', description: 'Enable/disable building the API documentation')
|
||||
option('tests', type: 'boolean', value: 'true', description: 'Enable/disable tests')
|
||||
|
|
|
|||
|
|
@ -1,92 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Copyright (C) 2021 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-->
|
||||
|
||||
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
|
||||
<!--
|
||||
org.freedesktop.impl.portal.EmulatedInput:
|
||||
@short_description: Emulated input backend interface
|
||||
|
||||
The emulated input portal enables applications to emulate input devices.
|
||||
-->
|
||||
<interface name="org.freedesktop.impl.portal.EmulatedInput">
|
||||
<!--
|
||||
CreateSession:
|
||||
@handle: Object path for the #org.freedesktop.impl.portal.Request object representing this call
|
||||
@session_handle: Object path for the #org.freedesktop.impl.portal.Session object representing the session being created
|
||||
@app_id: App id of the application
|
||||
@options: Vardict with optional further information
|
||||
@response: Numeric response
|
||||
@results: Vardict with the results of the call
|
||||
|
||||
Create an emulated input session
|
||||
|
||||
The following results get returned via the #org.freedesktop.portal.Request::Response signal:
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>session_id s</term>
|
||||
<listitem><para>
|
||||
The session id. A string representing the created screen cast session.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
-->
|
||||
<method name="CreateSession">
|
||||
<arg type="o" name="handle" direction="in"/>
|
||||
<arg type="o" name="session_handle" direction="in"/>
|
||||
<arg type="s" name="app_id" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In3" value="QVariantMap"/>
|
||||
<arg type="a{sv}" name="options" direction="in"/>
|
||||
<arg type="u" name="response" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
|
||||
<arg type="a{sv}" name="results" direction="out"/>
|
||||
</method>
|
||||
<!--
|
||||
Connect:
|
||||
@session_handle: Object path for the #org.freedesktop.portal.Session object
|
||||
@fd: File descriptor to be passed into libei. Only valid for a zero result.
|
||||
@app_id: App id of the application
|
||||
@options: Vardict with optional further information
|
||||
@response: Numeric response
|
||||
@results: Vardict with the results of the call
|
||||
|
||||
The following results get returned in results:
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>fd</term>
|
||||
<listitem><para>
|
||||
The file descriptor to th EIS implementation.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
Connect to the portal implementation's EIS backend.
|
||||
-->
|
||||
<method name="ConnectToEIS">
|
||||
<annotation name="org.gtk.GDBus.C.UnixFD" value="true"/>
|
||||
<arg type="o" name="session_handle" direction="in"/>
|
||||
<arg type="s" name="app_id" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In3" value="QVariantMap"/>
|
||||
<arg type="a{sv}" name="options" direction="in"/>
|
||||
<arg type="u" name="response" direction="out"/>
|
||||
<arg type="a{sv}" name="results" direction="out"/>
|
||||
</method>
|
||||
|
||||
<property name="version" type="u" access="read"/>
|
||||
</interface>
|
||||
</node>
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
Copyright (C) 2021 Red Hat, Inc.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
|
||||
<!--
|
||||
org.freedesktop.portal.Ei:
|
||||
@short_description: Emulated input portal
|
||||
|
||||
The emulated input portal enables applications to emulate input devices.
|
||||
-->
|
||||
<interface name="org.freedesktop.portal.EmulatedInput">
|
||||
<!--
|
||||
CreateSession:
|
||||
@options: Vardict with optional further information
|
||||
@handle: Object path for the #org.freedesktop.portal.Request object representing this call
|
||||
|
||||
Create a session to emulate input. A successfully created session can at
|
||||
any time be closed using org.freedesktop.portal.Session::Close, or may
|
||||
at any time be closed by the portal implementation, which will be
|
||||
signalled via org.freedesktop.portal.Session::Closed.
|
||||
|
||||
Supported keys in the @options vardict include:
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>handle_token s</term>
|
||||
<listitem><para>
|
||||
A string that will be used as the last element of the @handle. Must be a valid
|
||||
object path element. See the #org.freedesktop.portal.Request documentation for
|
||||
more information about the @handle.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>session_handle_token s</term>
|
||||
<listitem><para>
|
||||
A string that will be used as the last element of the session handle. Must be a valid
|
||||
object path element. See the #org.freedesktop.portal.Session documentation for
|
||||
more information about the session handle.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
The following results get returned via the #org.freedesktop.portal.Request::Response signal:
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>session_handle o</term>
|
||||
<listitem><para>
|
||||
The session handle. An object path for the
|
||||
#org.freedesktop.portal.Session object representing the created
|
||||
session.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
-->
|
||||
<method name="CreateSession">
|
||||
<arg type="a{sv}" name="options" direction="in"/>
|
||||
<arg type="o" name="handle" direction="out"/>
|
||||
</method>
|
||||
<!--
|
||||
ConnectToEIS:
|
||||
@session_handle: Object path for the #org.freedesktop.portal.Session object
|
||||
@options: Vardict with optional further information
|
||||
@response: Zero on success or 1 on error
|
||||
@results: Vardict with the results of the call
|
||||
|
||||
Open a file descriptor to the Emulated Input Server (EIS). This method
|
||||
is expected to complete immediately, it does not use a Request object.
|
||||
|
||||
This method will only succeed if the application has permission
|
||||
to emulate input.
|
||||
-->
|
||||
<method name="ConnectToEIS">
|
||||
<annotation name="org.gtk.GDBus.C.Name" value="connect_to_eis"/>
|
||||
<annotation name="org.gtk.GDBus.C.UnixFD" value="true"/>
|
||||
<arg type="o" name="session_handle" direction="in"/>
|
||||
<arg type="a{sv}" name="options" direction="in"/>
|
||||
<arg type="u" name="response" direction="out"/>
|
||||
<arg type="a{sv}" name="results" direction="out"/>
|
||||
</method>
|
||||
|
||||
<property name="version" type="u" access="read"/>
|
||||
</interface>
|
||||
</node>
|
||||
|
|
@ -1,357 +0,0 @@
|
|||
/* 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 <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#include "libei.h"
|
||||
#include "libei-private.h"
|
||||
|
||||
#include "util-io.h"
|
||||
#include "util-macros.h"
|
||||
#include "util-mem.h"
|
||||
#include "util-object.h"
|
||||
#include "util-strings.h"
|
||||
|
||||
struct ei_portal {
|
||||
struct object object;
|
||||
struct source *bus_source;
|
||||
sd_bus *bus;
|
||||
sd_bus_slot *slot;
|
||||
|
||||
char *busname;
|
||||
};
|
||||
|
||||
static void
|
||||
ei_portal_destroy(struct ei_portal *portal)
|
||||
{
|
||||
free(portal->busname);
|
||||
sd_bus_unref(portal->bus);
|
||||
}
|
||||
|
||||
static
|
||||
OBJECT_IMPLEMENT_CREATE(ei_portal);
|
||||
static
|
||||
OBJECT_IMPLEMENT_PARENT(ei_portal, ei);
|
||||
static
|
||||
OBJECT_IMPLEMENT_REF(ei_portal);
|
||||
static
|
||||
OBJECT_IMPLEMENT_UNREF_CLEANUP(ei_portal);
|
||||
|
||||
static void
|
||||
interface_portal_destroy(struct ei *ei, void *backend)
|
||||
{
|
||||
struct ei_portal *portal = backend;
|
||||
ei_portal_unref(portal);
|
||||
}
|
||||
|
||||
static const struct ei_backend_interface interface = {
|
||||
.destroy = interface_portal_destroy,
|
||||
};
|
||||
|
||||
static char *
|
||||
xdp_token(sd_bus *bus, char **token_out)
|
||||
{
|
||||
_cleanup_free_ char *sender = NULL;
|
||||
const char *name = NULL;
|
||||
|
||||
if (sd_bus_get_unique_name(bus, &name) != 0)
|
||||
return NULL;
|
||||
|
||||
sender = xstrdup(name + 1); /* drop initial : */
|
||||
|
||||
for (unsigned i = 0; sender[i]; i++) {
|
||||
if (sender[i] == '.')
|
||||
sender[i] = '_';
|
||||
}
|
||||
|
||||
char *token = xaprintf("ei_%d", rand());
|
||||
*token_out = token;
|
||||
|
||||
return xaprintf("/org/freedesktop/portal/desktop/request/%s/%s", sender, token);
|
||||
}
|
||||
|
||||
static void
|
||||
portal_connect(struct ei_portal *portal, const char *session_handle)
|
||||
{
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_unref_(sd_bus_message) *response = NULL;
|
||||
struct sd_bus *bus = portal->bus;
|
||||
struct ei *ei = ei_portal_parent(portal);
|
||||
int eisfd;
|
||||
|
||||
int rc = sd_bus_call_method(bus, portal->busname,
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.portal.EmulatedInput",
|
||||
"ConnectToEIS",
|
||||
&error,
|
||||
&response,
|
||||
"oa{sv}",
|
||||
session_handle,
|
||||
0);
|
||||
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to call method: %s", strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
int status;
|
||||
rc = sd_bus_message_read(response, "u", &status);
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to extract status, invalid message format: %s", strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status != 0) {
|
||||
log_info(ei, "Unable to get fd from portal");
|
||||
ei_disconnect(ei);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *key;
|
||||
rc = sd_bus_message_read(response, "a{sv}", 1, &key, "h", &eisfd);
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to extract fd, invalid message format: %s", strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!streq(key, "fd")) {
|
||||
log_error(ei, "Invalid key '%s', expected 'fd'", key);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* the fd is owned by the message */
|
||||
rc = xerrno(dup(eisfd));
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to dup fd: %s", strerror(-rc));
|
||||
goto out;
|
||||
} else {
|
||||
eisfd = rc;
|
||||
int flags = fcntl(eisfd, F_GETFL, 0);
|
||||
fcntl(eisfd, F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
|
||||
log_debug(ei, "Initiating ei context with fd %d from portal", eisfd);
|
||||
|
||||
/* We're done with DBus, lets clean up */
|
||||
source_remove(portal->bus_source);
|
||||
source_unref(portal->bus_source);
|
||||
portal->bus = sd_bus_unref(portal->bus);
|
||||
|
||||
rc = ei_set_connection(ei, eisfd);
|
||||
out:
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to set the connection: %s", strerror(-rc));
|
||||
ei_disconnect(ei);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
portal_response_received(sd_bus_message *m, void *userdata, sd_bus_error *error)
|
||||
{
|
||||
struct ei_portal *portal = userdata;
|
||||
struct ei *ei = ei_portal_parent(portal);
|
||||
unsigned response;
|
||||
|
||||
assert(m);
|
||||
assert(portal);
|
||||
|
||||
/* We'll only get this signal once */
|
||||
portal->slot = sd_bus_slot_unref(portal->slot);
|
||||
|
||||
int rc = sd_bus_message_read(m, "u", &response);
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to read response from signal: %s", strerror(-rc));
|
||||
ei_disconnect(ei);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug(ei, "Portal CreateSession reponse is %u", response);
|
||||
|
||||
const char *session_handle = NULL;
|
||||
if (response == 0) {
|
||||
const char *key;
|
||||
rc = sd_bus_message_read(m, "a{sv}", 1, &key, "s", &session_handle);
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to read session handle from signal: %s", strerror(-rc));
|
||||
ei_disconnect(ei);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!streq(key, "session_handle")) {
|
||||
log_error(ei, "Invalid or unhandled option: %ss", key);
|
||||
ei_disconnect(ei);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: we need to subscribe to the sessions Close signal */
|
||||
|
||||
if (response != 0) {
|
||||
ei_disconnect(ei);
|
||||
return 0;
|
||||
}
|
||||
|
||||
portal_connect(portal, session_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
session_closed_received(sd_bus_message *m, void *userdata, sd_bus_error *error)
|
||||
{
|
||||
struct ei_portal *portal = userdata;
|
||||
struct ei *ei = ei_portal_parent(portal);
|
||||
|
||||
log_error(ei, "Session closed received");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
dbus_dispatch(struct source *source, void *data)
|
||||
{
|
||||
struct ei_portal *portal = data;
|
||||
|
||||
/* We need to ref the bus here, portal_connect() may remove
|
||||
* portal->bus but that needs to stay valid here until the end of
|
||||
* the loop.
|
||||
*/
|
||||
_unref_(sd_bus) *bus = sd_bus_ref(portal->bus);
|
||||
|
||||
int rc;
|
||||
do {
|
||||
rc = sd_bus_process(bus, NULL);
|
||||
} while (rc > 0);
|
||||
|
||||
if (rc != 0) {
|
||||
log_error(ei_portal_parent(portal), "dbus processing failed with %s", strerror(-rc));
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
portal_init(struct ei *ei, const char *busname)
|
||||
{
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_unref_(sd_bus_message) *response = NULL;
|
||||
_unref_(sd_bus) *bus = NULL;
|
||||
_unref_(sd_bus_slot) *slot = NULL;
|
||||
_unref_(ei_portal) *portal = ei_portal_create(&ei->object);
|
||||
const char *path = NULL;
|
||||
|
||||
int rc = sd_bus_open_user(&bus);
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to init dbus: %s", strerror(-rc));
|
||||
return -ECONNREFUSED;
|
||||
}
|
||||
|
||||
_cleanup_free_ char *token = NULL;
|
||||
_cleanup_free_ char *handle = xdp_token(bus, &token);
|
||||
_cleanup_free_ char *session_token = xdp_token(bus, &session_token);
|
||||
_cleanup_free_ char *session_handle = xdp_token(bus, &session_token);
|
||||
|
||||
rc = sd_bus_match_signal(bus, &slot,
|
||||
busname,
|
||||
handle,
|
||||
"org.freedesktop.portal.Request",
|
||||
"Response",
|
||||
portal_response_received,
|
||||
portal);
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to subscribe to signal: %s", strerror(-rc));
|
||||
return -ECONNREFUSED;
|
||||
}
|
||||
|
||||
rc = sd_bus_match_signal(bus, &slot,
|
||||
busname,
|
||||
session_handle,
|
||||
"org.freedesktop.portal.Session",
|
||||
"Closed",
|
||||
session_closed_received,
|
||||
portal);
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to subscribe to signal: %s", strerror(-rc));
|
||||
return -ECONNREFUSED;
|
||||
}
|
||||
|
||||
rc = sd_bus_call_method(bus,
|
||||
busname,
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.portal.EmulatedInput",
|
||||
"CreateSession",
|
||||
&error,
|
||||
&response,
|
||||
"a{sv}", 2,
|
||||
"handle_token", /* string key */
|
||||
"s", token, /* variant string */
|
||||
"session_handle_token", /* string key */
|
||||
"s", session_token /* variant string */
|
||||
);
|
||||
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to call method: %s", strerror(-rc));
|
||||
return -ECONNREFUSED;
|
||||
}
|
||||
|
||||
rc = sd_bus_message_read(response, "o", &path);
|
||||
if (rc < 0) {
|
||||
log_error(ei, "Failed to parse response: %s", strerror(-rc));
|
||||
return -ECONNREFUSED;
|
||||
}
|
||||
|
||||
log_debug(ei, "portal Response object is %s", path);
|
||||
|
||||
struct source *s = source_new(sd_bus_get_fd(bus), dbus_dispatch, portal);
|
||||
source_never_close_fd(s); /* the bus object handles the fd */
|
||||
rc = sink_add_source(ei->sink, s);
|
||||
if (rc == 0) {
|
||||
portal->bus_source = source_ref(s);
|
||||
portal->bus = sd_bus_ref(bus);
|
||||
portal->slot = sd_bus_slot_ref(slot);
|
||||
}
|
||||
|
||||
portal->busname = xstrdup(busname);
|
||||
|
||||
ei->backend = ei_portal_ref(portal);
|
||||
ei->backend_interface = interface;
|
||||
|
||||
source_unref(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_public_ int
|
||||
ei_setup_backend_portal(struct ei *ei)
|
||||
{
|
||||
return portal_init(ei, "org.freedesktop.portal.Desktop");
|
||||
}
|
||||
|
||||
_public_ int
|
||||
ei_setup_backend_portal_busname(struct ei *ei, const char *busname)
|
||||
{
|
||||
return portal_init(ei, busname);
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/* 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 "libei.h"
|
||||
|
||||
#include "util-macros.h"
|
||||
|
||||
#if !ENABLE_LIBEI_PORTAL
|
||||
_public_ int
|
||||
ei_setup_backend_portal(struct ei *ei)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
20
src/libei.h
20
src/libei.h
|
|
@ -581,26 +581,6 @@ ei_setup_backend_socket(struct ei *ei, const char *socketpath);
|
|||
int
|
||||
ei_setup_backend_fd(struct ei *ei, int fd);
|
||||
|
||||
/**
|
||||
* Connect to the `org.freedesktop.portal.Desktop` portal.
|
||||
*
|
||||
* @return 0 on success or a negative errno on failure
|
||||
*/
|
||||
int
|
||||
ei_setup_backend_portal(struct ei *ei);
|
||||
|
||||
/**
|
||||
* Connect to an `org.freedesktop.portal.Desktop` implementation on the
|
||||
* given busname.
|
||||
*
|
||||
* Outside of testing environments, there is usually no reason to use
|
||||
* this function, use ei_setup_backend_portal() instead.
|
||||
*
|
||||
* @return 0 on success or a negative errno on failure
|
||||
*/
|
||||
int
|
||||
ei_setup_backend_portal_busname(struct ei *ei, const char *busname);
|
||||
|
||||
/**
|
||||
* Increase the refcount of this struct by one. Use ei_unref() to decrease
|
||||
* the refcount.
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@
|
|||
*
|
||||
* Usually, you'd want to:
|
||||
* - run the eis-demo-server (or some other EIS implementation)
|
||||
* - run the eis-fake-portal (if testing portal clients), otherwise
|
||||
* export LIBEI_SOCKET=eis-0, or whatever value was given.
|
||||
* - export LIBEI_SOCKET=eis-0, or whatever value was given
|
||||
* - run the ei-demo-client
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@
|
|||
*
|
||||
* Usually, you'd want to:
|
||||
* - run the eis-demo-server (or some other EIS implementation)
|
||||
* - run the eis-fake-portal (if testing portal clients), otherwise
|
||||
* export LIBEI_SOCKET=eis-0, or whatever value was given.
|
||||
* - export LIBEI_SOCKET=eis-0, or whatever value was given
|
||||
* - run the ei-demo-client
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@
|
|||
*
|
||||
* Usually, you'd want to:
|
||||
* - run the eis-demo-server (or some other EIS implementation)
|
||||
* - run the eis-fake-portal (if testing portal clients), otherwise
|
||||
* export LIBEI_SOCKET=eis-0, or whatever value was given.
|
||||
* - export LIBEI_SOCKET=eis-0, or whatever value was given
|
||||
* - run the libei client
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -1,377 +0,0 @@
|
|||
/* SPDX-License-Identifier: MIT */
|
||||
/*
|
||||
* Copyright © 2021 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.
|
||||
*/
|
||||
|
||||
/* A simple tool that sets up the org.freedesktop.impl.portal.EmulatedInput
|
||||
* DBus interface and provides the required functionality.
|
||||
*
|
||||
* This tool is useful for testing portals (e.g.
|
||||
* xdg-desktop-portal) that need to connect to impl.portal. It provides
|
||||
* enough data to succeed with the connection.
|
||||
*
|
||||
* This tool does not run an EIS server, use e.g. the eis-demo-server.
|
||||
*
|
||||
* Usually, you'd want to:
|
||||
* - run the eis-demo-server (or some other EIS implementation)
|
||||
* - run the eis-fake-impl-portal
|
||||
* - run xdg-desktop-portal
|
||||
* - run the libei client relying on the portal
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util-io.h"
|
||||
#include "util-list.h"
|
||||
#include "util-mem.h"
|
||||
#include "util-logger.h"
|
||||
#include "util-strings.h"
|
||||
|
||||
#include "libreis.h"
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
DEFINE_UNREF_CLEANUP_FUNC(reis);
|
||||
|
||||
struct session {
|
||||
unsigned int version;
|
||||
struct list link;
|
||||
struct sd_bus_slot *slot;
|
||||
char *handle;
|
||||
};
|
||||
|
||||
struct portal {
|
||||
unsigned int version;
|
||||
struct logger *logger;
|
||||
char *busname;
|
||||
|
||||
struct list sessions;
|
||||
} portal;
|
||||
|
||||
|
||||
#define call(_call) do { \
|
||||
int _rc = _call; \
|
||||
if (_rc < 0) { \
|
||||
log_error(portal, "Failed with %s %s:%d\n", strerror(-_rc), __func__, __LINE__); \
|
||||
return _rc; \
|
||||
} } while(0)
|
||||
|
||||
static int
|
||||
session_close(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
|
||||
{
|
||||
sd_bus_reply_method_return(m, "");
|
||||
/* Not implemented */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const sd_bus_vtable session_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("Close", "", "", session_close, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_PROPERTY("version", "u", NULL, offsetof(struct session, version), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
static int
|
||||
create_session_object(struct portal *portal,
|
||||
sd_bus *bus,
|
||||
const char *objectpath)
|
||||
{
|
||||
struct session *session = calloc(sizeof *session, 1);
|
||||
|
||||
session->handle = xstrdup(objectpath);
|
||||
|
||||
call(sd_bus_add_object_vtable(bus, &session->slot,
|
||||
objectpath,
|
||||
"org.freedesktop.impl.portal.Session",
|
||||
session_vtable,
|
||||
session));
|
||||
|
||||
list_append(&portal->sessions, &session->link);
|
||||
|
||||
log_debug(portal, "Session created on %s\n", objectpath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
request_close(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
|
||||
{
|
||||
/* We don't live long enough for this to be called */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const sd_bus_vtable request_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("Close", "", "", request_close, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
static int
|
||||
create_request_object(struct portal *portal,
|
||||
sd_bus *bus,
|
||||
const char *objectpath,
|
||||
const char *session_path)
|
||||
{
|
||||
_unref_(sd_bus_slot) *slot = NULL;
|
||||
|
||||
create_session_object(portal, bus, session_path);
|
||||
|
||||
call(sd_bus_add_object_vtable(bus, &slot,
|
||||
objectpath,
|
||||
"org.freedesktop.impl.portal.Request",
|
||||
request_vtable,
|
||||
NULL));
|
||||
|
||||
int response = 0;
|
||||
log_debug(portal, "emitting Response %d on %s\n", response, objectpath);
|
||||
return sd_bus_emit_signal(bus,
|
||||
objectpath,
|
||||
"org.freedesktop.impl.portal.Request",
|
||||
"Response",
|
||||
"ua{sv}",
|
||||
response,
|
||||
1,
|
||||
"session_handle",/* string key */
|
||||
"s", session_path /* variant string */
|
||||
);
|
||||
/* note: _unref_ removes object immediately */
|
||||
}
|
||||
|
||||
static int
|
||||
portal_create_session(sd_bus_message *m, void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct portal *portal = userdata;
|
||||
static int session_id = 1234;
|
||||
|
||||
const char *handle;
|
||||
const char *session_handle;
|
||||
const char *app_id;
|
||||
|
||||
sd_bus_message_read(m, "oosa{sv}",
|
||||
&handle,
|
||||
&session_handle,
|
||||
&app_id, 0);
|
||||
|
||||
log_debug(portal, "CreateSession: app-id %s, session %s, handle %s\n", app_id, session_handle, handle);
|
||||
|
||||
_cleanup_free_ char *sid = xaprintf("%d", session_id++);
|
||||
|
||||
call(sd_bus_reply_method_return(m, "ua{sv}", 0, 1, "session_id", "s", sid));
|
||||
|
||||
return create_request_object(portal, sd_bus_message_get_bus(m), handle, session_handle);
|
||||
}
|
||||
|
||||
static void
|
||||
set_prop_cmdline(struct reis *reis)
|
||||
{
|
||||
_cleanup_free_ char *cmdline = cmdline_as_str();
|
||||
reis_set_property_with_permissions(reis, "ei.application.cmdline", cmdline, REIS_PROPERTY_PERM_NONE);
|
||||
}
|
||||
|
||||
static void
|
||||
set_prop_pid(struct reis *reis)
|
||||
{
|
||||
char pid[64];
|
||||
|
||||
xsnprintf(pid, sizeof(pid), "%i", getpid());
|
||||
reis_set_property_with_permissions(reis, "ei.application.pid", pid, REIS_PROPERTY_PERM_NONE);
|
||||
}
|
||||
|
||||
static void
|
||||
set_prop_type(struct reis *reis)
|
||||
{
|
||||
reis_set_property_with_permissions(reis, "ei.connection.type", "portal", REIS_PROPERTY_PERM_NONE);
|
||||
}
|
||||
|
||||
static int
|
||||
portal_connect_to_eis(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
|
||||
{
|
||||
struct portal *portal = userdata;
|
||||
|
||||
log_debug(portal, "Got a connect request\n");
|
||||
|
||||
const char *xdg = getenv("XDG_RUNTIME_DIR");
|
||||
if (!xdg)
|
||||
return -ENOENT;
|
||||
|
||||
const char *session_handle;
|
||||
const char *app_id;
|
||||
call(sd_bus_message_read(m, "os", &session_handle, &app_id));
|
||||
|
||||
log_debug(portal, "Connect request session %s appid %s\n", session_handle, app_id);
|
||||
|
||||
struct session *session;
|
||||
bool found = false;
|
||||
list_for_each_safe(session, &portal->sessions, link) {
|
||||
if (streq(session->handle, session_handle)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We're aborting here because it's a client bug and this is just a
|
||||
* fake portal */
|
||||
if (!found) {
|
||||
log_error(portal, "Invalid session handle %s\n", session_handle);
|
||||
abort();
|
||||
}
|
||||
|
||||
log_error(portal, "Valid session %s, connecting to EIS\n", session_handle);
|
||||
|
||||
_cleanup_free_ char *sockpath = xaprintf("%s/eis-0", xdg);
|
||||
int fd = xconnect(sockpath);
|
||||
if (fd < 0) {
|
||||
log_error(portal, "Failed to connect to EIS (%s)\n", strerror(-fd));
|
||||
return sd_bus_reply_method_return(m, "ua{sv}", 1, 0);
|
||||
}
|
||||
|
||||
_unref_(reis) *reis = reis_new(fd);
|
||||
assert(reis);
|
||||
set_prop_pid(reis);
|
||||
set_prop_cmdline(reis);
|
||||
set_prop_type(reis);
|
||||
|
||||
log_debug(portal, "passing fd %d\n", fd);
|
||||
return sd_bus_reply_method_return(m, "ua{sv}", 0,
|
||||
1, /* array length */
|
||||
"fd", "h", fd /* name: "fd", type handle, value fd */);
|
||||
}
|
||||
|
||||
static const sd_bus_vtable portal_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("CreateSession", "oosa{sv}", "ua{sv}", portal_create_session, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("ConnectToEIS", "osa{sv}", "ua{sv}", portal_connect_to_eis, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_PROPERTY("version", "u", NULL, offsetof(struct portal, version), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
static int
|
||||
run(struct portal *portal)
|
||||
{
|
||||
_unref_(sd_bus) *bus = NULL;
|
||||
_unref_(sd_bus_slot) *slot = NULL;
|
||||
int rc = sd_bus_open_user(&bus);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = sd_bus_add_object_vtable(bus, &slot,
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.impl.portal.EmulatedInput",
|
||||
portal_vtable,
|
||||
portal);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
sd_bus_add_object_manager(bus, NULL, "/org/freedesktop/portal/desktop");
|
||||
|
||||
log_debug(portal, "Portal object at: /org/freedesktop/ei/EmulatedInput\n");
|
||||
|
||||
rc = sd_bus_request_name(bus, portal->busname, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
log_debug(portal, "Portal DBus name: %s\n", portal->busname);
|
||||
|
||||
_unref_(sd_event) *event = NULL;
|
||||
rc = sd_event_default(&event);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = sd_event_set_watchdog(event, true);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = sd_bus_attach_event(bus, event, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return sd_event_loop(event);
|
||||
}
|
||||
|
||||
static void
|
||||
usage(FILE *fp, const char *argv0)
|
||||
{
|
||||
fprintf(fp,
|
||||
"Usage: %s [--busname=a.b.c.d]\n"
|
||||
"\n"
|
||||
"Emulates an XDG Desktop portal for the org.freedesktop.impl.portal.EmulatedInput interface\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" --busname use the given busname instead of the default org.freedesktop.impl.portal.eis.EmulatedInput\n"
|
||||
"",
|
||||
basename(argv0));
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
_cleanup_free_ char *busname = xstrdup("org.freedesktop.impl.portal.eis.EmulatedInput");
|
||||
|
||||
while (1) {
|
||||
enum opts {
|
||||
OPT_BUSNAME,
|
||||
};
|
||||
static struct option long_opts[] = {
|
||||
{ "busname", required_argument, 0, OPT_BUSNAME},
|
||||
{ "help", no_argument, 0, 'h'},
|
||||
{ .name = NULL },
|
||||
};
|
||||
|
||||
int optind = 0;
|
||||
int c = getopt_long(argc, argv, "h", long_opts, &optind);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch(c) {
|
||||
case 'h':
|
||||
usage(stdout, argv[0]);
|
||||
return EXIT_SUCCESS;
|
||||
case OPT_BUSNAME:
|
||||
free(busname);
|
||||
busname = xstrdup(optarg);
|
||||
break;
|
||||
default:
|
||||
usage(stderr, argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
list_init(&portal.sessions);
|
||||
portal.version = 1;
|
||||
portal.busname = steal(&busname);
|
||||
portal.logger = logger_new("impl.portal", NULL);
|
||||
logger_set_priority(portal.logger, LOGGER_DEBUG);
|
||||
|
||||
int rc = run(&portal);
|
||||
if (rc < 0)
|
||||
fprintf(stderr, "Failed to start fake portal: %s\n", strerror(-rc));
|
||||
|
||||
logger_unref(portal.logger);
|
||||
free(portal.busname);
|
||||
return rc == 0;
|
||||
}
|
||||
|
|
@ -1,417 +0,0 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
/* A simple tool that sets up the org.freedesktop.portal.EmulatedInput DBus
|
||||
* interface and provides the required functionality.
|
||||
*
|
||||
* This tool is useful for testing the libei portal backend, it provides just
|
||||
* enough data to have that backend succeed with the connection.
|
||||
*
|
||||
* This tool does not run an EIS server, use e.g. the eis-demo-server.
|
||||
*
|
||||
* Usually, you'd want to:
|
||||
* - run the eis-demo-server (or some other EIS implementation)
|
||||
* - run the eis-fake-portal
|
||||
* - run the libei client relying on the portal
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util-io.h"
|
||||
#include "util-list.h"
|
||||
#include "util-mem.h"
|
||||
#include "util-logger.h"
|
||||
#include "util-strings.h"
|
||||
|
||||
#include "libreis.h"
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
DEFINE_UNREF_CLEANUP_FUNC(reis);
|
||||
|
||||
struct session {
|
||||
struct list link;
|
||||
struct sd_bus_slot *slot;
|
||||
char *handle;
|
||||
};
|
||||
|
||||
struct portal {
|
||||
struct logger *logger;
|
||||
char *busname;
|
||||
|
||||
struct list sessions;
|
||||
} portal;
|
||||
|
||||
|
||||
#define call(_call) do { \
|
||||
int _rc = _call; \
|
||||
if (_rc < 0) { \
|
||||
log_error(portal, "Failed with %s %s:%d\n", strerror(-_rc), __func__, __LINE__); \
|
||||
return _rc; \
|
||||
} } while(0)
|
||||
|
||||
static int
|
||||
session_close(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
|
||||
{
|
||||
/* Not implemented */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const sd_bus_vtable session_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("Close", "", "", session_close, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_SIGNAL("Closed", "ua{sv}", 0),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
static int
|
||||
create_session_object(struct portal *portal,
|
||||
sd_bus *bus,
|
||||
const char *objectpath)
|
||||
{
|
||||
struct session *session = calloc(sizeof *session, 1);
|
||||
|
||||
session->handle = xstrdup(objectpath);
|
||||
|
||||
call(sd_bus_add_object_vtable(bus, &session->slot,
|
||||
objectpath,
|
||||
"org.freedesktop.portal.Session",
|
||||
session_vtable,
|
||||
NULL));
|
||||
|
||||
list_append(&portal->sessions, &session->link);
|
||||
|
||||
log_debug(portal, "Session created on %s\n", objectpath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
request_close(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
|
||||
{
|
||||
/* We don't live long enough for this to be called */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const sd_bus_vtable request_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("Close", "", "", request_close, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_SIGNAL("Response", "ua{sv}", 0),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
static int
|
||||
create_request_object(struct portal *portal,
|
||||
sd_bus *bus,
|
||||
const char *objectpath,
|
||||
const char *session_path)
|
||||
{
|
||||
_unref_(sd_bus_slot) *slot = NULL;
|
||||
|
||||
create_session_object(portal, bus, session_path);
|
||||
|
||||
call(sd_bus_add_object_vtable(bus, &slot,
|
||||
objectpath,
|
||||
"org.freedesktop.portal.Request",
|
||||
request_vtable,
|
||||
NULL));
|
||||
|
||||
int response = 0;
|
||||
log_debug(portal, "emitting Response %d on %s\n", response, objectpath);
|
||||
return sd_bus_emit_signal(bus,
|
||||
objectpath,
|
||||
"org.freedesktop.portal.Request",
|
||||
"Response",
|
||||
"ua{sv}",
|
||||
response,
|
||||
1,
|
||||
"session_handle",/* string key */
|
||||
"s", session_path /* variant string */
|
||||
);
|
||||
/* note: _unref_ removes object immediately */
|
||||
}
|
||||
|
||||
static char *
|
||||
sender_token(const char *input)
|
||||
{
|
||||
if (!input)
|
||||
return NULL;
|
||||
|
||||
char *token = strstrip(input, ":");
|
||||
for (size_t idx = 0; token[idx]; idx++) {
|
||||
if (token[idx] == '.')
|
||||
token[idx] = '_';
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
static int
|
||||
portal_create_session(sd_bus_message *m, void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct portal *portal = userdata;
|
||||
|
||||
call(sd_bus_message_enter_container(m, 'a', "{sv}"));
|
||||
|
||||
const char *session_token = NULL;
|
||||
const char *handle_token = NULL;
|
||||
|
||||
const char *key, *val;
|
||||
call(sd_bus_message_read(m, "{sv}", &key, "s", &val));
|
||||
if (streq(key, "handle_token"))
|
||||
handle_token = val;
|
||||
else if (streq(key, "session_handle_token"))
|
||||
session_token = val;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
call(sd_bus_message_read(m, "{sv}", &key, "s", &val));
|
||||
if (streq(key, "handle_token"))
|
||||
handle_token = val;
|
||||
else if (streq(key, "session_handle_token"))
|
||||
session_token = val;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
assert(handle_token);
|
||||
assert(session_token);
|
||||
|
||||
call(sd_bus_message_exit_container(m));
|
||||
|
||||
_cleanup_free_ char *sender = sender_token(sd_bus_message_get_sender(m));
|
||||
if (!sender)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Send back the object path of the object we're about to create. We
|
||||
* then create the object, so if that fails we have a problem but
|
||||
* meh, this is for testing only .*/
|
||||
_cleanup_free_ char *objpath = xaprintf("%s/request/%s/%s",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
sender,
|
||||
handle_token);
|
||||
call(sd_bus_reply_method_return(m, "o", objpath));
|
||||
|
||||
_cleanup_free_ char *session_path = xaprintf("%s/session/%s/%s",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
sender,
|
||||
session_token);
|
||||
|
||||
/* now create the object */
|
||||
return create_request_object(portal, sd_bus_message_get_bus(m), objpath, session_path);
|
||||
}
|
||||
|
||||
static void
|
||||
set_prop_cmdline(struct reis *reis)
|
||||
{
|
||||
_cleanup_free_ char *cmdline = cmdline_as_str();
|
||||
reis_set_property_with_permissions(reis, "ei.application.cmdline", cmdline, REIS_PROPERTY_PERM_NONE);
|
||||
}
|
||||
|
||||
static void
|
||||
set_prop_pid(struct reis *reis)
|
||||
{
|
||||
char pid[64];
|
||||
|
||||
xsnprintf(pid, sizeof(pid), "%i", getpid());
|
||||
reis_set_property_with_permissions(reis, "ei.application.pid", pid, REIS_PROPERTY_PERM_NONE);
|
||||
}
|
||||
|
||||
static void
|
||||
set_prop_type(struct reis *reis)
|
||||
{
|
||||
reis_set_property_with_permissions(reis, "ei.connection.type", "portal", REIS_PROPERTY_PERM_NONE);
|
||||
}
|
||||
|
||||
static int
|
||||
portal_connect_to_eis(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
|
||||
{
|
||||
struct portal *portal = userdata;
|
||||
|
||||
const char *xdg = getenv("XDG_RUNTIME_DIR");
|
||||
if (!xdg)
|
||||
return -ENOENT;
|
||||
|
||||
const char *session_handle;
|
||||
|
||||
call(sd_bus_message_read(m, "oa{sv}", &session_handle, 0));
|
||||
|
||||
struct session *session;
|
||||
bool found = false;
|
||||
list_for_each_safe(session, &portal->sessions, link) {
|
||||
if (streq(session->handle, session_handle)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We're aborting here because it's a client bug and this is just a
|
||||
* fake portal */
|
||||
if (!found) {
|
||||
log_error(portal, "Invalid session handle %s\n", session_handle);
|
||||
return sd_bus_reply_method_return(m, "ua{sv}", 1, 0);
|
||||
}
|
||||
|
||||
log_info(portal, "Valid session %s, connecting to EIS\n", session_handle);
|
||||
|
||||
_cleanup_free_ char *sockpath = xstrdup(getenv("LIBEI_SOCKET"));
|
||||
if (!sockpath)
|
||||
sockpath = xaprintf("%s/eis-0", xdg);
|
||||
|
||||
int handle = xconnect(sockpath);
|
||||
if (handle < 0) {
|
||||
log_error(portal, "Failed to connect to EIS (%s)\n", strerror(-handle));
|
||||
return sd_bus_reply_method_return(m, "ua{sv}", 1, 0);
|
||||
}
|
||||
|
||||
_unref_(reis) *reis = reis_new(handle);
|
||||
assert(reis);
|
||||
set_prop_pid(reis);
|
||||
set_prop_cmdline(reis);
|
||||
set_prop_type(reis);
|
||||
|
||||
log_debug(portal, "passing Handle %d\n", handle);
|
||||
return sd_bus_reply_method_return(m, "ua{sv}", 0,
|
||||
1, /* array size */
|
||||
"fd", "h", handle);
|
||||
}
|
||||
|
||||
static const sd_bus_vtable portal_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("CreateSession", "a{sv}", "o", portal_create_session, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("ConnectToEIS", "oa{sv}", "ua{sv}", portal_connect_to_eis, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
static int
|
||||
run(struct portal *portal)
|
||||
{
|
||||
_unref_(sd_bus) *bus = NULL;
|
||||
_unref_(sd_bus_slot) *slot = NULL;
|
||||
int rc = sd_bus_open_user(&bus);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = sd_bus_add_object_vtable(bus, &slot,
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.portal.EmulatedInput",
|
||||
portal_vtable,
|
||||
portal);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
log_debug(portal, "Portal object at: /org/freedesktop/portal/desktop\n");
|
||||
|
||||
rc = sd_bus_request_name(bus, portal->busname, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
log_debug(portal, "Portal DBus name: %s\n", portal->busname);
|
||||
|
||||
_unref_(sd_event) *event = NULL;
|
||||
rc = sd_event_default(&event);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = sd_event_set_watchdog(event, true);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = sd_bus_attach_event(bus, event, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return sd_event_loop(event);
|
||||
}
|
||||
|
||||
static void
|
||||
usage(FILE *fp, const char *argv0)
|
||||
{
|
||||
fprintf(fp,
|
||||
"Usage: %s [--busname=a.b.c.d]\n"
|
||||
"\n"
|
||||
"Emulates an XDG Desktop portal for the org.freedesktop.portal.EmulatedInput interface\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" --busname use the given busname instead of the default org.freedesktop.portal.Desktop\n"
|
||||
"\n"
|
||||
"This portal connects directly to the EIS instance at $LIBEI_SOCKET or, if unset, at \n"
|
||||
"$XDG_RUNTIME_DIR/eis-0. It does **not** use the org.freedesktop.impl.portal.EmulatedInput\n"
|
||||
"interface.\n"
|
||||
"",
|
||||
basename(argv0));
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
_cleanup_free_ char *busname = xstrdup("org.freedesktop.portal.Desktop");
|
||||
|
||||
while (1) {
|
||||
enum opts {
|
||||
OPT_BUSNAME,
|
||||
};
|
||||
static struct option long_opts[] = {
|
||||
{ "busname", required_argument, 0, OPT_BUSNAME},
|
||||
{ "help", no_argument, 0, 'h'},
|
||||
{ .name = NULL },
|
||||
};
|
||||
|
||||
int optind = 0;
|
||||
int c = getopt_long(argc, argv, "h", long_opts, &optind);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch(c) {
|
||||
case 'h':
|
||||
usage(stdout, argv[0]);
|
||||
return EXIT_SUCCESS;
|
||||
case OPT_BUSNAME:
|
||||
free(busname);
|
||||
busname = xstrdup(optarg);
|
||||
break;
|
||||
default:
|
||||
usage(stderr, argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
list_init(&portal.sessions);
|
||||
portal.busname = steal(&busname);
|
||||
portal.logger = logger_new("portal", NULL);
|
||||
logger_set_priority(portal.logger, LOGGER_DEBUG);
|
||||
|
||||
int rc = run(&portal);
|
||||
if (rc < 0)
|
||||
fprintf(stderr, "Failed to start fake portal: %s\n", strerror(-rc));
|
||||
|
||||
logger_unref(portal.logger);
|
||||
free(portal.busname);
|
||||
return rc == 0;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue