From d14c7abe11044f52ff112d7fcb0f011447f944c2 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 30 Jan 2018 15:11:39 +0100 Subject: [PATCH] protocol: make connect async Make the protocol client connect call async with a callback when it completes. Move the connect methods into separate files, add an empty connect method that will use the screencast portal to get a pipewire fd. Use the remote intention to get the connect method. Add some better error reporting. --- src/modules/meson.build | 2 + src/modules/module-protocol-native.c | 71 +++----------- src/modules/module-protocol-native/defs.h | 25 +++++ .../module-protocol-native/local-socket.c | 98 +++++++++++++++++++ .../portal-screencast.c | 38 +++++++ src/pipewire/protocol.h | 10 +- src/pipewire/remote.c | 25 +++-- 7 files changed, 203 insertions(+), 66 deletions(-) create mode 100644 src/modules/module-protocol-native/defs.h create mode 100644 src/modules/module-protocol-native/local-socket.c create mode 100644 src/modules/module-protocol-native/portal-screencast.c diff --git a/src/modules/meson.build b/src/modules/meson.build index 6d816d4b8..c47e798cd 100644 --- a/src/modules/meson.build +++ b/src/modules/meson.build @@ -69,6 +69,8 @@ pipewire_module_link_factory = shared_library('pipewire-module-link-factory', pipewire_module_protocol_native = shared_library('pipewire-module-protocol-native', [ 'module-protocol-native.c', + 'module-protocol-native/local-socket.c', + 'module-protocol-native/portal-screencast.c', 'module-protocol-native/protocol-native.c', 'module-protocol-native/connection.c' ], c_args : pipewire_module_c_args, diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c index d4352a327..8d39bf639 100644 --- a/src/modules/module-protocol-native.c +++ b/src/modules/module-protocol-native.c @@ -47,6 +47,7 @@ #include "extensions/protocol-native.h" #include "modules/module-protocol-native/connection.h" +#include "modules/module-protocol-native/defs.h" #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 108 @@ -477,60 +478,6 @@ static bool add_socket(struct pw_protocol *protocol, struct server *s) } -static const char * -get_remote(const struct pw_properties *properties) -{ - const char *name = NULL; - - if (properties) - name = pw_properties_get(properties, PW_REMOTE_PROP_REMOTE_NAME); - if (name == NULL) - name = getenv("PIPEWIRE_REMOTE"); - if (name == NULL) - name = "pipewire-0"; - return name; -} - -static int impl_connect(struct pw_protocol_client *client) -{ - struct client *impl = SPA_CONTAINER_OF(client, struct client, this); - struct sockaddr_un addr; - socklen_t size; - const char *runtime_dir, *name = NULL; - int name_size, fd; - - if ((runtime_dir = getenv("XDG_RUNTIME_DIR")) == NULL) { - pw_log_error("connect failed: XDG_RUNTIME_DIR not set in the environment"); - return -1; - } - - name = get_remote(impl->properties); - - if ((fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) - return -1; - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_LOCAL; - name_size = snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", runtime_dir, name) + 1; - - if (name_size > (int) sizeof addr.sun_path) { - pw_log_error("socket path \"%s/%s\" plus null terminator exceeds 108 bytes", - runtime_dir, name); - goto error_close; - }; - - size = offsetof(struct sockaddr_un, sun_path) + name_size; - - if (connect(fd, (struct sockaddr *) &addr, size) < 0) - goto error_close; - - return pw_protocol_client_connect_fd(client, fd); - - error_close: - close(fd); - return -1; -} - static int impl_steal_fd(struct pw_protocol_client *client) { struct client *impl = SPA_CONTAINER_OF(client, struct client, this); @@ -660,12 +607,14 @@ static int impl_connect_fd(struct pw_protocol_client *client, int fd) fd, SPA_IO_IN | SPA_IO_HUP | SPA_IO_ERR, true, on_remote_data, impl); + if (impl->source == NULL) + goto error_close; return 0; error_close: close(fd); - return -1; + return -ENOMEM; } static void impl_disconnect(struct pw_protocol_client *client) @@ -707,6 +656,7 @@ impl_new_client(struct pw_protocol *protocol, { struct client *impl; struct pw_protocol_client *this; + const char *str = NULL; if ((impl = calloc(1, sizeof(struct client))) == NULL) return NULL; @@ -717,7 +667,16 @@ impl_new_client(struct pw_protocol *protocol, impl->properties = properties ? pw_properties_copy(properties) : NULL; - this->connect = impl_connect; + if (properties) + str = pw_properties_get(properties, "remote.intention"); + if (str == NULL) + str = "generic"; + + if (!strcmp(str, "screencast")) + this->connect = pw_protocol_native_connect_portal_screencast; + else + this->connect = pw_protocol_native_connect_local_socket; + this->steal_fd = impl_steal_fd; this->connect_fd = impl_connect_fd; this->disconnect = impl_disconnect; diff --git a/src/modules/module-protocol-native/defs.h b/src/modules/module-protocol-native/defs.h new file mode 100644 index 000000000..079284cc1 --- /dev/null +++ b/src/modules/module-protocol-native/defs.h @@ -0,0 +1,25 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +int pw_protocol_native_connect_local_socket(struct pw_protocol_client *client, + void (*done_callback) (void *data, int res), + void *data); +int pw_protocol_native_connect_portal_screencast(struct pw_protocol_client *client, + void (*done_callback) (void *data, int res), + void *data); diff --git a/src/modules/module-protocol-native/local-socket.c b/src/modules/module-protocol-native/local-socket.c new file mode 100644 index 000000000..694ed75be --- /dev/null +++ b/src/modules/module-protocol-native/local-socket.c @@ -0,0 +1,98 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +static const char * +get_remote(const struct pw_properties *properties) +{ + const char *name = NULL; + + if (properties) + name = pw_properties_get(properties, PW_REMOTE_PROP_REMOTE_NAME); + if (name == NULL) + name = getenv("PIPEWIRE_REMOTE"); + if (name == NULL) + name = "pipewire-0"; + return name; +} + +int pw_protocol_native_connect_local_socket(struct pw_protocol_client *client, + void (*done_callback) (void *data, int res), + void *data) +{ + struct pw_remote *remote = client->remote; + struct sockaddr_un addr; + socklen_t size; + const char *runtime_dir, *name = NULL; + int res, name_size, fd; + + if ((runtime_dir = getenv("XDG_RUNTIME_DIR")) == NULL) { + pw_log_error("connect failed: XDG_RUNTIME_DIR not set in the environment"); + return -EIO; + } + + name = get_remote(pw_remote_get_properties(remote)); + + if ((fd = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) + return -errno; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_LOCAL; + name_size = snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", runtime_dir, name) + 1; + + if (name_size > (int) sizeof addr.sun_path) { + pw_log_error("socket path \"%s/%s\" plus null terminator exceeds 108 bytes", + runtime_dir, name); + res = -ENOSPC; + goto error_close; + }; + + size = offsetof(struct sockaddr_un, sun_path) + name_size; + + if (connect(fd, (struct sockaddr *) &addr, size) < 0) { + res = -errno; + goto error_close; + } + + res = pw_protocol_client_connect_fd(client, fd); + + done_callback(data, res); + + return res; + + error_close: + close(fd); + return res; +} diff --git a/src/modules/module-protocol-native/portal-screencast.c b/src/modules/module-protocol-native/portal-screencast.c new file mode 100644 index 000000000..72967371a --- /dev/null +++ b/src/modules/module-protocol-native/portal-screencast.c @@ -0,0 +1,38 @@ +/* PipeWire + * Copyright (C) 2018 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +int pw_protocol_native_connect_portal_screencast(struct pw_protocol_client *client, + void (*done_callback) (void *data, int res), + void *data) +{ + return -ENOTSUP; +} diff --git a/src/pipewire/protocol.h b/src/pipewire/protocol.h index c7e00d1e4..2b6592d1a 100644 --- a/src/pipewire/protocol.h +++ b/src/pipewire/protocol.h @@ -41,16 +41,18 @@ struct pw_protocol_client { struct pw_remote *remote; /**< the associated remote */ - int (*connect) (struct pw_protocol_client *client); - int (*steal_fd) (struct pw_protocol_client *client); + int (*connect) (struct pw_protocol_client *client, + void (*done_callback) (void *data, int result), + void *data); int (*connect_fd) (struct pw_protocol_client *client, int fd); + int (*steal_fd) (struct pw_protocol_client *client); void (*disconnect) (struct pw_protocol_client *client); void (*destroy) (struct pw_protocol_client *client); }; -#define pw_protocol_client_connect(c) ((c)->connect(c)) -#define pw_protocol_client_steal_fd(c) ((c)->steal_fd(c)) +#define pw_protocol_client_connect(c,cb,d) ((c)->connect(c,cb,d)) #define pw_protocol_client_connect_fd(c,fd) ((c)->connect_fd(c,fd)) +#define pw_protocol_client_steal_fd(c) ((c)->steal_fd(c)) #define pw_protocol_client_disconnect(c) ((c)->disconnect(c)) #define pw_protocol_client_destroy(c) ((c)->destroy(c)) diff --git a/src/pipewire/remote.c b/src/pipewire/remote.c index dde8ec851..9ff6fba4d 100644 --- a/src/pipewire/remote.c +++ b/src/pipewire/remote.c @@ -364,7 +364,7 @@ static int do_connect(struct pw_remote *remote) no_proxy: pw_protocol_client_disconnect(remote->conn); pw_remote_update_state(remote, PW_REMOTE_STATE_ERROR, "can't connect: no memory"); - return -1; + return -ENOMEM; } struct pw_core_proxy * pw_remote_get_core_proxy(struct pw_remote *remote) @@ -382,18 +382,30 @@ struct pw_proxy *pw_remote_find_proxy(struct pw_remote *remote, uint32_t id) return pw_map_lookup(&remote->objects, id); } +static void done_connect(void *data, int result) +{ + struct pw_remote *remote = data; + if (result < 0) { + pw_remote_update_state(remote, PW_REMOTE_STATE_ERROR, "can't connect: %s", + spa_strerror(result)); + return; + } + + do_connect(remote); +} + int pw_remote_connect(struct pw_remote *remote) { int res; pw_remote_update_state(remote, PW_REMOTE_STATE_CONNECTING, NULL); - if ((res = pw_protocol_client_connect (remote->conn)) < 0) { - pw_remote_update_state(remote, PW_REMOTE_STATE_ERROR, "connect failed"); + if ((res = pw_protocol_client_connect (remote->conn, done_connect, remote)) < 0) { + pw_remote_update_state(remote, PW_REMOTE_STATE_ERROR, + "connect failed %s", spa_strerror(res)); return res; } - - return do_connect(remote); + return remote->state == PW_REMOTE_STATE_ERROR ? -EIO : 0; } int pw_remote_connect_fd(struct pw_remote *remote, int fd) @@ -403,7 +415,8 @@ int pw_remote_connect_fd(struct pw_remote *remote, int fd) pw_remote_update_state(remote, PW_REMOTE_STATE_CONNECTING, NULL); if ((res = pw_protocol_client_connect_fd (remote->conn, fd)) < 0) { - pw_remote_update_state(remote, PW_REMOTE_STATE_ERROR, "connect_fd failed"); + pw_remote_update_state(remote, PW_REMOTE_STATE_ERROR, + "connect_fd failed %s", spa_strerror(res)); return res; }