mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	remote: remove events and states
This commit is contained in:
		
							parent
							
								
									8a959ea7a1
								
							
						
					
					
						commit
						3d79970d88
					
				
					 5 changed files with 5 additions and 158 deletions
				
			
		| 
						 | 
					@ -93,7 +93,8 @@ int pw_protocol_native_connect_local_socket(struct pw_protocol_client *client,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res = pw_protocol_client_connect_fd(client, fd, true);
 | 
						res = pw_protocol_client_connect_fd(client, fd, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	done_callback(data, res);
 | 
						if (done_callback)
 | 
				
			||||||
 | 
							done_callback(data, res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return res;
 | 
						return res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -753,11 +753,6 @@ struct pw_remote {
 | 
				
			||||||
	int recv_seq;				/**< last received sequence number */
 | 
						int recv_seq;				/**< last received sequence number */
 | 
				
			||||||
	int send_seq;				/**< last protocol result code */
 | 
						int send_seq;				/**< last protocol result code */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	enum pw_remote_state state;
 | 
					 | 
				
			||||||
	char *error;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct spa_hook_list listener_list;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void *user_data;			/**< extra user data */
 | 
						void *user_data;			/**< extra user data */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,59 +49,6 @@ struct remote {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** \endcond */
 | 
					/** \endcond */
 | 
				
			||||||
 | 
					 | 
				
			||||||
SPA_EXPORT
 | 
					 | 
				
			||||||
const char *pw_remote_state_as_string(enum pw_remote_state state)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	switch (state) {
 | 
					 | 
				
			||||||
	case PW_REMOTE_STATE_ERROR:
 | 
					 | 
				
			||||||
		return "error";
 | 
					 | 
				
			||||||
	case PW_REMOTE_STATE_UNCONNECTED:
 | 
					 | 
				
			||||||
		return "unconnected";
 | 
					 | 
				
			||||||
	case PW_REMOTE_STATE_CONNECTING:
 | 
					 | 
				
			||||||
		return "connecting";
 | 
					 | 
				
			||||||
	case PW_REMOTE_STATE_CONNECTED:
 | 
					 | 
				
			||||||
		return "connected";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return "invalid-state";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int
 | 
					 | 
				
			||||||
pw_remote_update_state(struct pw_remote *remote, enum pw_remote_state state, const char *fmt, ...)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	enum pw_remote_state old = remote->state;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (old != state) {
 | 
					 | 
				
			||||||
		free(remote->error);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (fmt) {
 | 
					 | 
				
			||||||
			va_list varargs;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			va_start(varargs, fmt);
 | 
					 | 
				
			||||||
			if (vasprintf(&remote->error, fmt, varargs) < 0) {
 | 
					 | 
				
			||||||
				pw_log_debug(NAME" %p: error formating message: %m", remote);
 | 
					 | 
				
			||||||
				remote->error = NULL;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			va_end(varargs);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			remote->error = NULL;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if (state == PW_REMOTE_STATE_ERROR) {
 | 
					 | 
				
			||||||
			pw_log_error(NAME" %p: update state from %s -> %s (%s)", remote,
 | 
					 | 
				
			||||||
				     pw_remote_state_as_string(old),
 | 
					 | 
				
			||||||
				     pw_remote_state_as_string(state), remote->error);
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			pw_log_debug(NAME" %p: update state from %s -> %s", remote,
 | 
					 | 
				
			||||||
				     pw_remote_state_as_string(old),
 | 
					 | 
				
			||||||
				     pw_remote_state_as_string(state));
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		remote->state = state;
 | 
					 | 
				
			||||||
		pw_remote_emit_state_changed(remote, old, state, remote->error);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void core_event_ping(void *data, uint32_t id, int seq)
 | 
					static void core_event_ping(void *data, uint32_t id, int seq)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct pw_remote *this = data;
 | 
						struct pw_remote *this = data;
 | 
				
			||||||
| 
						 | 
					@ -222,15 +169,11 @@ struct pw_remote *pw_remote_new(struct pw_core *core,
 | 
				
			||||||
	pw_fill_remote_properties(core, properties);
 | 
						pw_fill_remote_properties(core, properties);
 | 
				
			||||||
	this->properties = properties;
 | 
						this->properties = properties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	this->state = PW_REMOTE_STATE_UNCONNECTED;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pw_map_init(&this->objects, 64, 32);
 | 
						pw_map_init(&this->objects, 64, 32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_list_init(&this->stream_list);
 | 
						spa_list_init(&this->stream_list);
 | 
				
			||||||
	spa_list_init(&this->filter_list);
 | 
						spa_list_init(&this->filter_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_hook_list_init(&this->listener_list);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((protocol_name = pw_properties_get(properties, PW_KEY_PROTOCOL)) == NULL) {
 | 
						if ((protocol_name = pw_properties_get(properties, PW_KEY_PROTOCOL)) == NULL) {
 | 
				
			||||||
		if ((protocol_name = pw_properties_get(core->properties, PW_KEY_PROTOCOL)) == NULL) {
 | 
							if ((protocol_name = pw_properties_get(core->properties, PW_KEY_PROTOCOL)) == NULL) {
 | 
				
			||||||
			protocol_name = PW_TYPE_INFO_PROTOCOL_Native;
 | 
								protocol_name = PW_TYPE_INFO_PROTOCOL_Native;
 | 
				
			||||||
| 
						 | 
					@ -284,10 +227,8 @@ void pw_remote_destroy(struct pw_remote *remote)
 | 
				
			||||||
	struct pw_filter *filter;
 | 
						struct pw_filter *filter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_log_debug(NAME" %p: destroy", remote);
 | 
						pw_log_debug(NAME" %p: destroy", remote);
 | 
				
			||||||
	pw_remote_emit_destroy(remote);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (remote->state != PW_REMOTE_STATE_UNCONNECTED)
 | 
						pw_remote_disconnect(remote);
 | 
				
			||||||
		pw_remote_disconnect(remote);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_list_consume(stream, &remote->stream_list, link)
 | 
						spa_list_consume(stream, &remote->stream_list, link)
 | 
				
			||||||
		pw_stream_destroy(stream);
 | 
							pw_stream_destroy(stream);
 | 
				
			||||||
| 
						 | 
					@ -302,7 +243,6 @@ void pw_remote_destroy(struct pw_remote *remote)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_log_debug(NAME" %p: free", remote);
 | 
						pw_log_debug(NAME" %p: free", remote);
 | 
				
			||||||
	pw_properties_free(remote->properties);
 | 
						pw_properties_free(remote->properties);
 | 
				
			||||||
	free(remote->error);
 | 
					 | 
				
			||||||
	free(impl);
 | 
						free(impl);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -342,23 +282,6 @@ void *pw_remote_get_user_data(struct pw_remote *remote)
 | 
				
			||||||
	return remote->user_data;
 | 
						return remote->user_data;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SPA_EXPORT
 | 
					 | 
				
			||||||
enum pw_remote_state pw_remote_get_state(struct pw_remote *remote, const char **error)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (error)
 | 
					 | 
				
			||||||
		*error = remote->error;
 | 
					 | 
				
			||||||
	return remote->state;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SPA_EXPORT
 | 
					 | 
				
			||||||
void pw_remote_add_listener(struct pw_remote *remote,
 | 
					 | 
				
			||||||
			    struct spa_hook *listener,
 | 
					 | 
				
			||||||
			    const struct pw_remote_events *events,
 | 
					 | 
				
			||||||
			    void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	spa_hook_list_append(&remote->listener_list, listener, events, data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void core_proxy_destroy(void *data)
 | 
					static void core_proxy_destroy(void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct pw_remote *remote = data;
 | 
						struct pw_remote *remote = data;
 | 
				
			||||||
| 
						 | 
					@ -418,19 +341,9 @@ static int init_connect(struct pw_remote *remote)
 | 
				
			||||||
error_clean_core_proxy:
 | 
					error_clean_core_proxy:
 | 
				
			||||||
	pw_proxy_remove((struct pw_proxy*)remote->core_proxy);
 | 
						pw_proxy_remove((struct pw_proxy*)remote->core_proxy);
 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	pw_remote_update_state(remote, PW_REMOTE_STATE_ERROR, "can't connect: %s", spa_strerror(res));
 | 
					 | 
				
			||||||
	return res;
 | 
						return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					 | 
				
			||||||
do_connect(struct spa_loop *loop,
 | 
					 | 
				
			||||||
		bool async, uint32_t seq, const void *data, size_t size, void *user_data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct pw_remote *remote = user_data;
 | 
					 | 
				
			||||||
	pw_remote_update_state(remote, PW_REMOTE_STATE_CONNECTED, NULL);
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SPA_EXPORT
 | 
					SPA_EXPORT
 | 
				
			||||||
struct pw_core_proxy * pw_remote_get_core_proxy(struct pw_remote *remote)
 | 
					struct pw_core_proxy * pw_remote_get_core_proxy(struct pw_remote *remote)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -449,37 +362,20 @@ struct pw_proxy *pw_remote_find_proxy(struct pw_remote *remote, uint32_t id)
 | 
				
			||||||
	return pw_map_lookup(&remote->objects, 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;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	pw_loop_invoke(remote->core->main_loop,
 | 
					 | 
				
			||||||
			do_connect, 0, NULL, 0, false, remote);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
SPA_EXPORT
 | 
					SPA_EXPORT
 | 
				
			||||||
int pw_remote_connect(struct pw_remote *remote)
 | 
					int pw_remote_connect(struct pw_remote *remote)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int res;
 | 
						int res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_remote_update_state(remote, PW_REMOTE_STATE_CONNECTING, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((res = init_connect(remote)) < 0)
 | 
						if ((res = init_connect(remote)) < 0)
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((res = pw_protocol_client_connect(remote->conn,
 | 
						if ((res = pw_protocol_client_connect(remote->conn,
 | 
				
			||||||
					&remote->properties->dict,
 | 
										&remote->properties->dict,
 | 
				
			||||||
					done_connect, remote)) < 0)
 | 
										NULL, NULL)) < 0)
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return remote->state == PW_REMOTE_STATE_ERROR ? -EIO : 0;
 | 
					 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	pw_remote_update_state(remote, PW_REMOTE_STATE_ERROR,
 | 
					 | 
				
			||||||
			"connect failed %d: %s", res, spa_strerror(res));
 | 
					 | 
				
			||||||
	return res;
 | 
						return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -488,21 +384,13 @@ int pw_remote_connect_fd(struct pw_remote *remote, int fd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int res;
 | 
						int res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_remote_update_state(remote, PW_REMOTE_STATE_CONNECTING, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if ((res = init_connect(remote)) < 0)
 | 
						if ((res = init_connect(remote)) < 0)
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((res = pw_protocol_client_connect_fd(remote->conn, fd, true)) < 0)
 | 
						if ((res = pw_protocol_client_connect_fd(remote->conn, fd, true)) < 0)
 | 
				
			||||||
		goto error;
 | 
							goto error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_loop_invoke(remote->core->main_loop,
 | 
					 | 
				
			||||||
			do_connect, 0, NULL, 0, false, remote);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return remote->state == PW_REMOTE_STATE_ERROR ? -EIO : 0;
 | 
					 | 
				
			||||||
error:
 | 
					error:
 | 
				
			||||||
	pw_remote_update_state(remote, PW_REMOTE_STATE_ERROR,
 | 
					 | 
				
			||||||
			"connect_fd failed %d: %s", res, spa_strerror(res));
 | 
					 | 
				
			||||||
	return res;
 | 
						return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -510,10 +398,7 @@ SPA_EXPORT
 | 
				
			||||||
int pw_remote_steal_fd(struct pw_remote *remote)
 | 
					int pw_remote_steal_fd(struct pw_remote *remote)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int fd;
 | 
						int fd;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	fd = pw_protocol_client_steal_fd(remote->conn);
 | 
						fd = pw_protocol_client_steal_fd(remote->conn);
 | 
				
			||||||
	if (fd >= 0)
 | 
					 | 
				
			||||||
		pw_remote_update_state(remote, PW_REMOTE_STATE_UNCONNECTED, NULL);
 | 
					 | 
				
			||||||
	return fd;
 | 
						return fd;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -538,8 +423,6 @@ int pw_remote_disconnect(struct pw_remote *remote)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	remote->client_proxy = NULL;
 | 
						remote->client_proxy = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_remote_update_state(remote, PW_REMOTE_STATE_UNCONNECTED, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pw_map_for_each(&remote->objects, destroy_proxy, remote);
 | 
						pw_map_for_each(&remote->objects, destroy_proxy, remote);
 | 
				
			||||||
	pw_map_reset(&remote->objects);
 | 
						pw_map_reset(&remote->objects);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,29 +116,6 @@ struct pw_remote;
 | 
				
			||||||
#include <pipewire/node.h>
 | 
					#include <pipewire/node.h>
 | 
				
			||||||
#include <pipewire/proxy.h>
 | 
					#include <pipewire/proxy.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** \enum pw_remote_state The state of a \ref pw_remote \memberof pw_remote */
 | 
					 | 
				
			||||||
enum pw_remote_state {
 | 
					 | 
				
			||||||
	PW_REMOTE_STATE_ERROR = -1,		/**< remote is in error */
 | 
					 | 
				
			||||||
	PW_REMOTE_STATE_UNCONNECTED = 0,	/**< not connected */
 | 
					 | 
				
			||||||
	PW_REMOTE_STATE_CONNECTING = 1,		/**< connecting to remote PipeWire */
 | 
					 | 
				
			||||||
	PW_REMOTE_STATE_CONNECTED = 2,		/**< remote is connected and ready */
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Convert a \ref pw_remote_state to a readable string \memberof pw_remote */
 | 
					 | 
				
			||||||
const char *pw_remote_state_as_string(enum pw_remote_state state);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Events for the remote. use \ref pw_remote_add_listener */
 | 
					 | 
				
			||||||
struct pw_remote_events {
 | 
					 | 
				
			||||||
#define PW_VERSION_REMOTE_EVENTS	0
 | 
					 | 
				
			||||||
	uint32_t version;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** The remote is destroyed */
 | 
					 | 
				
			||||||
	void (*destroy)	(void *data);
 | 
					 | 
				
			||||||
	/** emited when the state changes */
 | 
					 | 
				
			||||||
	void (*state_changed) (void *data, enum pw_remote_state old,
 | 
					 | 
				
			||||||
			       enum pw_remote_state state, const char *error);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Create a new unconnected remote \memberof pw_remote
 | 
					/** Create a new unconnected remote \memberof pw_remote
 | 
				
			||||||
 * \return a new unconnected remote */
 | 
					 * \return a new unconnected remote */
 | 
				
			||||||
struct pw_remote *
 | 
					struct pw_remote *
 | 
				
			||||||
| 
						 | 
					@ -162,15 +139,6 @@ int pw_remote_update_properties(struct pw_remote *remote, const struct spa_dict
 | 
				
			||||||
/** Get the user_data. The size was given in \ref pw_remote_new */
 | 
					/** Get the user_data. The size was given in \ref pw_remote_new */
 | 
				
			||||||
void *pw_remote_get_user_data(struct pw_remote *remote);
 | 
					void *pw_remote_get_user_data(struct pw_remote *remote);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Get the current state, \a error is set when state is \ref PW_REMOTE_STATE_ERROR */
 | 
					 | 
				
			||||||
enum pw_remote_state pw_remote_get_state(struct pw_remote *remote, const char **error);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Add listener for events */
 | 
					 | 
				
			||||||
void pw_remote_add_listener(struct pw_remote *remote,
 | 
					 | 
				
			||||||
			    struct spa_hook *listener,
 | 
					 | 
				
			||||||
			    const struct pw_remote_events *events,
 | 
					 | 
				
			||||||
			    void *data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Connect to a remote PipeWire \memberof pw_remote
 | 
					/** Connect to a remote PipeWire \memberof pw_remote
 | 
				
			||||||
 * \return 0 on success, < 0 on error */
 | 
					 * \return 0 on success, < 0 on error */
 | 
				
			||||||
int pw_remote_connect(struct pw_remote *remote);
 | 
					int pw_remote_connect(struct pw_remote *remote);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ test_apps = [
 | 
				
			||||||
	'test-core',
 | 
						'test-core',
 | 
				
			||||||
	'test-interfaces',
 | 
						'test-interfaces',
 | 
				
			||||||
	'test-properties',
 | 
						'test-properties',
 | 
				
			||||||
	'test-remote',
 | 
						#	'test-remote',
 | 
				
			||||||
	'test-stream',
 | 
						'test-stream',
 | 
				
			||||||
	'test-utils'
 | 
						'test-utils'
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue