chromium: fix launcher for halibut
[AGL/meta-agl-devel.git] / meta-pipewire / recipes-multimedia / pipewire / pipewire / 0002-extensions-implement-Endpoint-ClientEndpoint-interfa.patch
1 From 5afe82a430642c2f7e116941709a624b8fd73011 Mon Sep 17 00:00:00 2001
2 From: George Kiagiadakis <george.kiagiadakis@collabora.com>
3 Date: Thu, 23 May 2019 18:59:05 +0300
4 Subject: [PATCH] extensions: implement Endpoint & ClientEndpoint interfaces
5
6 The ClientEndpoint interface allows session managers to register
7 endpoint objects on pipewire.
8 The Endpoint interface allows other clients to interact with
9 endpoints provided by the session manager.
10
11 Upstream-Status: Pending
12 ---
13  src/extensions/client-endpoint.h              | 106 ++++
14  src/extensions/endpoint.h                     | 237 +++++++++
15  src/extensions/meson.build                    |   2 +
16  src/modules/meson.build                       |  12 +
17  src/modules/module-endpoint.c                 | 137 +++++
18  src/modules/module-endpoint/endpoint-impl.c   | 428 ++++++++++++++++
19  src/modules/module-endpoint/endpoint-impl.h   |  52 ++
20  src/modules/module-endpoint/protocol-native.c | 472 ++++++++++++++++++
21  src/pipewire/pipewire.c                       |   2 +
22  src/pipewire/type.h                           |   3 +-
23  10 files changed, 1450 insertions(+), 1 deletion(-)
24  create mode 100644 src/extensions/client-endpoint.h
25  create mode 100644 src/extensions/endpoint.h
26  create mode 100644 src/modules/module-endpoint.c
27  create mode 100644 src/modules/module-endpoint/endpoint-impl.c
28  create mode 100644 src/modules/module-endpoint/endpoint-impl.h
29  create mode 100644 src/modules/module-endpoint/protocol-native.c
30
31 diff --git a/src/extensions/client-endpoint.h b/src/extensions/client-endpoint.h
32 new file mode 100644
33 index 00000000..0389893c
34 --- /dev/null
35 +++ b/src/extensions/client-endpoint.h
36 @@ -0,0 +1,106 @@
37 +/* PipeWire
38 + *
39 + * Copyright © 2019 Collabora Ltd.
40 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
41 + *
42 + * Permission is hereby granted, free of charge, to any person obtaining a
43 + * copy of this software and associated documentation files (the "Software"),
44 + * to deal in the Software without restriction, including without limitation
45 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
46 + * and/or sell copies of the Software, and to permit persons to whom the
47 + * Software is furnished to do so, subject to the following conditions:
48 + *
49 + * The above copyright notice and this permission notice (including the next
50 + * paragraph) shall be included in all copies or substantial portions of the
51 + * Software.
52 + *
53 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
54 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
55 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
56 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
57 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
58 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
59 + * DEALINGS IN THE SOFTWARE.
60 + */
61 +
62 +#ifndef PIPEWIRE_EXT_CLIENT_ENDPOINT_H
63 +#define PIPEWIRE_EXT_CLIENT_ENDPOINT_H
64 +
65 +#include "endpoint.h"
66 +
67 +#ifdef __cplusplus
68 +extern "C" {
69 +#endif
70 +
71 +struct pw_client_endpoint_proxy;
72 +
73 +#define PW_VERSION_CLIENT_ENDPOINT                     0
74 +#define PW_EXTENSION_MODULE_CLIENT_ENDPOINT            PIPEWIRE_MODULE_PREFIX "module-endpoint"
75 +
76 +#define PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE         0
77 +#define PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM            1
78 +
79 +struct pw_client_endpoint_proxy_methods {
80 +#define PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS       0
81 +       uint32_t version;
82 +
83 +       /**
84 +        * Update endpoint info
85 +        */
86 +       int (*update) (void *object,
87 +#define PW_CLIENT_ENDPOINT_UPDATE_PARAMS               (1 << 0)
88 +#define PW_CLIENT_ENDPOINT_UPDATE_PARAMS_INCREMENTAL   (1 << 1)
89 +#define PW_CLIENT_ENDPOINT_UPDATE_INFO                 (1 << 2)
90 +                       uint32_t change_mask,
91 +                       uint32_t n_params,
92 +                       const struct spa_pod **params,
93 +                       const struct pw_endpoint_info *info);
94 +};
95 +
96 +static inline int
97 +pw_client_endpoint_proxy_update(struct pw_client_endpoint_proxy *p,
98 +                               uint32_t change_mask,
99 +                               uint32_t n_params,
100 +                               const struct spa_pod **params,
101 +                               struct pw_endpoint_info *info)
102 +{
103 +       return pw_proxy_do((struct pw_proxy*)p,
104 +                          struct pw_client_endpoint_proxy_methods, update,
105 +                          change_mask, n_params, params, info);
106 +}
107 +
108 +#define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM       0
109 +#define PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM             1
110 +
111 +struct pw_client_endpoint_proxy_events {
112 +#define PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS        0
113 +       uint32_t version;
114 +
115 +       /**
116 +        * Set a parameter on the endpoint
117 +        *
118 +        * \param id the parameter id to set
119 +        * \param flags extra parameter flags
120 +        * \param param the parameter to set
121 +        */
122 +       void (*set_param) (void *object, uint32_t id, uint32_t flags,
123 +                          const struct spa_pod *param);
124 +};
125 +
126 +static inline void
127 +pw_client_endpoint_proxy_add_listener(struct pw_client_endpoint_proxy *p,
128 +                               struct spa_hook *listener,
129 +                               const struct pw_client_endpoint_proxy_events *events,
130 +                               void *data)
131 +{
132 +       pw_proxy_add_proxy_listener((struct pw_proxy*)p, listener, events, data);
133 +}
134 +
135 +#define pw_client_endpoint_resource_set_param(r,...)   \
136 +       pw_resource_notify(r,struct pw_client_endpoint_proxy_events,set_param,__VA_ARGS__)
137 +
138 +#ifdef __cplusplus
139 +}  /* extern "C" */
140 +#endif
141 +
142 +#endif /* PIPEWIRE_EXT_CLIENT_ENDPOINT_H */
143 diff --git a/src/extensions/endpoint.h b/src/extensions/endpoint.h
144 new file mode 100644
145 index 00000000..211c0895
146 --- /dev/null
147 +++ b/src/extensions/endpoint.h
148 @@ -0,0 +1,237 @@
149 +/* PipeWire
150 + *
151 + * Copyright © 2019 Collabora Ltd.
152 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
153 + *
154 + * Permission is hereby granted, free of charge, to any person obtaining a
155 + * copy of this software and associated documentation files (the "Software"),
156 + * to deal in the Software without restriction, including without limitation
157 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
158 + * and/or sell copies of the Software, and to permit persons to whom the
159 + * Software is furnished to do so, subject to the following conditions:
160 + *
161 + * The above copyright notice and this permission notice (including the next
162 + * paragraph) shall be included in all copies or substantial portions of the
163 + * Software.
164 + *
165 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
166 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
167 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
168 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
169 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
170 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
171 + * DEALINGS IN THE SOFTWARE.
172 + */
173 +
174 +#ifndef PIPEWIRE_EXT_ENDPOINT_H
175 +#define PIPEWIRE_EXT_ENDPOINT_H
176 +
177 +#include <spa/utils/defs.h>
178 +#include <spa/utils/type-info.h>
179 +#include <pipewire/proxy.h>
180 +
181 +#ifdef __cplusplus
182 +extern "C" {
183 +#endif
184 +
185 +struct pw_endpoint_proxy;
186 +
187 +#define PW_VERSION_ENDPOINT                    0
188 +#define PW_EXTENSION_MODULE_ENDPOINT           PIPEWIRE_MODULE_PREFIX "module-endpoint"
189 +
190 +/* extending enum spa_param_type */
191 +enum endpoint_param_type {
192 +       PW_ENDPOINT_PARAM_EnumControl = 0x1000,
193 +       PW_ENDPOINT_PARAM_Control,
194 +       PW_ENDPOINT_PARAM_EnumStream,
195 +};
196 +
197 +enum endpoint_param_object_type {
198 +       PW_ENDPOINT_OBJECT_ParamControl = PW_TYPE_FIRST + SPA_TYPE_OBJECT_START + 0x1001,
199 +       PW_ENDPOINT_OBJECT_ParamStream,
200 +};
201 +
202 +/** properties for PW_ENDPOINT_OBJECT_ParamControl */
203 +enum endpoint_param_control {
204 +       PW_ENDPOINT_PARAM_CONTROL_START,        /**< object id, one of enum endpoint_param_type */
205 +       PW_ENDPOINT_PARAM_CONTROL_id,           /**< control id (Int) */
206 +       PW_ENDPOINT_PARAM_CONTROL_stream_id,    /**< stream id (Int) */
207 +       PW_ENDPOINT_PARAM_CONTROL_name,         /**< control name (String) */
208 +       PW_ENDPOINT_PARAM_CONTROL_type,         /**< control type (Range) */
209 +       PW_ENDPOINT_PARAM_CONTROL_value,        /**< control value */
210 +};
211 +
212 +/** properties for PW_ENDPOINT_OBJECT_ParamStream */
213 +enum endpoint_param_stream {
214 +       PW_ENDPOINT_PARAM_STREAM_START,         /**< object id, one of enum endpoint_param_type */
215 +       PW_ENDPOINT_PARAM_STREAM_id,            /**< stream id (Int) */
216 +       PW_ENDPOINT_PARAM_STREAM_name,          /**< stream name (String) */
217 +};
218 +
219 +static const struct spa_type_info endpoint_param_type_info[] = {
220 +       { PW_ENDPOINT_PARAM_EnumControl, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ID_BASE "EnumControl", NULL },
221 +       { PW_ENDPOINT_PARAM_Control, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ID_BASE "Control", NULL },
222 +       { PW_ENDPOINT_PARAM_EnumStream, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_ID_BASE "EnumStream", NULL },
223 +       { 0, 0, NULL, NULL },
224 +};
225 +
226 +#define PW_ENDPOINT_TYPE_INFO_ParamControl             SPA_TYPE_INFO_PARAM_BASE "ParamControl"
227 +#define PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE       PW_ENDPOINT_TYPE_INFO_ParamControl ":"
228 +
229 +static const struct spa_type_info endpoint_param_control_info[] = {
230 +       { PW_ENDPOINT_PARAM_CONTROL_START, SPA_TYPE_Id, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE, spa_type_param, },
231 +       { PW_ENDPOINT_PARAM_CONTROL_id, SPA_TYPE_Int, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE "id", NULL },
232 +       { PW_ENDPOINT_PARAM_CONTROL_stream_id, SPA_TYPE_Int, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE "streamId", NULL },
233 +       { PW_ENDPOINT_PARAM_CONTROL_name, SPA_TYPE_String, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE "name", NULL },
234 +       { PW_ENDPOINT_PARAM_CONTROL_type, SPA_TYPE_Pod, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE "type", NULL },
235 +       { PW_ENDPOINT_PARAM_CONTROL_value, SPA_TYPE_Struct, PW_ENDPOINT_TYPE_INFO_PARAM_CONTROL_BASE "value", NULL },
236 +       { 0, 0, NULL, NULL },
237 +};
238 +
239 +#define PW_ENDPOINT_TYPE_INFO_ParamStream              SPA_TYPE_INFO_PARAM_BASE "ParamStream"
240 +#define PW_ENDPOINT_TYPE_INFO_PARAM_STREAM_BASE                PW_ENDPOINT_TYPE_INFO_ParamStream ":"
241 +
242 +static const struct spa_type_info endpoint_param_stream_info[] = {
243 +       { PW_ENDPOINT_PARAM_STREAM_START, SPA_TYPE_Id, PW_ENDPOINT_TYPE_INFO_PARAM_STREAM_BASE, spa_type_param, },
244 +       { PW_ENDPOINT_PARAM_STREAM_id, SPA_TYPE_Int, PW_ENDPOINT_TYPE_INFO_PARAM_STREAM_BASE "id", NULL },
245 +       { PW_ENDPOINT_PARAM_STREAM_name, SPA_TYPE_String, PW_ENDPOINT_TYPE_INFO_PARAM_STREAM_BASE "name", NULL },
246 +       { 0, 0, NULL, NULL },
247 +};
248 +
249 +static const struct spa_type_info endpoint_param_object_type_info[] = {
250 +       { PW_ENDPOINT_OBJECT_ParamControl, SPA_TYPE_Object, SPA_TYPE_INFO_OBJECT_BASE "ParamControl", endpoint_param_control_info, },
251 +       { PW_ENDPOINT_OBJECT_ParamStream, SPA_TYPE_Object, SPA_TYPE_INFO_OBJECT_BASE "ParamStream", endpoint_param_stream_info },
252 +       { 0, 0, NULL, NULL },
253 +};
254 +
255 +struct pw_endpoint_info {
256 +       uint32_t id;                            /**< id of the global */
257 +#define PW_ENDPOINT_CHANGE_MASK_PARAMS         (1 << 0)
258 +#define PW_ENDPOINT_CHANGE_MASK_PROPS          (1 << 1)
259 +       uint32_t change_mask;                   /**< bitfield of changed fields since last call */
260 +       uint32_t n_params;                      /**< number of items in \a params */
261 +       struct spa_param_info *params;          /**< parameters */
262 +       struct spa_dict *props;                 /**< extra properties */
263 +};
264 +
265 +#define PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS      0
266 +#define PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS           1
267 +#define PW_ENDPOINT_PROXY_METHOD_SET_PARAM             2
268 +#define PW_ENDPOINT_PROXY_METHOD_NUM                   3
269 +
270 +struct pw_endpoint_proxy_methods {
271 +#define PW_VERSION_ENDPOINT_PROXY_METHODS              0
272 +       uint32_t version;
273 +
274 +       /**
275 +        * Subscribe to parameter changes
276 +        *
277 +        * Automatically emit param events for the given ids when
278 +        * they are changed.
279 +        *
280 +        * \param ids an array of param ids
281 +        * \param n_ids the number of ids in \a ids
282 +        */
283 +       int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids);
284 +
285 +       /**
286 +        * Enumerate endpoint parameters
287 +        *
288 +        * Start enumeration of endpoint parameters. For each param, a
289 +        * param event will be emited.
290 +        *
291 +        * \param seq a sequence number to place in the reply
292 +        * \param id the parameter id to enum or SPA_ID_INVALID for all
293 +        * \param start the start index or 0 for the first param
294 +        * \param num the maximum number of params to retrieve
295 +        * \param filter a param filter or NULL
296 +        */
297 +       int (*enum_params) (void *object, int seq,
298 +                           uint32_t id, uint32_t start, uint32_t num,
299 +                           const struct spa_pod *filter);
300 +
301 +       /**
302 +        * Set a parameter on the endpoint
303 +        *
304 +        * \param id the parameter id to set
305 +        * \param flags extra parameter flags
306 +        * \param param the parameter to set
307 +        */
308 +       int (*set_param) (void *object, uint32_t id, uint32_t flags,
309 +                         const struct spa_pod *param);
310 +};
311 +
312 +static inline int
313 +pw_endpoint_proxy_subscribe_params(struct pw_endpoint_proxy *p, uint32_t *ids, uint32_t n_ids)
314 +{
315 +       return pw_proxy_do((struct pw_proxy*)p, struct pw_endpoint_proxy_methods,
316 +                          subscribe_params, ids, n_ids);
317 +}
318 +
319 +static inline int
320 +pw_endpoint_proxy_enum_params(struct pw_endpoint_proxy *p, int seq,
321 +                               uint32_t id, uint32_t start, uint32_t num,
322 +                               const struct spa_pod *filter)
323 +{
324 +       return pw_proxy_do((struct pw_proxy*)p, struct pw_endpoint_proxy_methods,
325 +                           enum_params, seq, id, start, num, filter);
326 +}
327 +
328 +static inline int
329 +pw_endpoint_proxy_set_param(struct pw_endpoint_proxy *p, uint32_t id,
330 +                           uint32_t flags, const struct spa_pod *param)
331 +{
332 +       return pw_proxy_do((struct pw_proxy*)p, struct pw_endpoint_proxy_methods,
333 +                          set_param, id, flags, param);
334 +}
335 +
336 +#define PW_ENDPOINT_PROXY_EVENT_INFO           0
337 +#define PW_ENDPOINT_PROXY_EVENT_PARAM          1
338 +#define PW_ENDPOINT_PROXY_EVENT_NUM            2
339 +
340 +struct pw_endpoint_proxy_events {
341 +#define PW_VERSION_ENDPOINT_PROXY_EVENTS       0
342 +       uint32_t version;
343 +
344 +       /**
345 +        * Notify endpoint info
346 +        *
347 +        * \param info info about the endpoint
348 +        */
349 +       void (*info) (void *object, const struct pw_endpoint_info * info);
350 +
351 +       /**
352 +        * Notify an endpoint param
353 +        *
354 +        * Event emited as a result of the enum_params method.
355 +        *
356 +        * \param seq the sequence number of the request
357 +        * \param id the param id
358 +        * \param index the param index
359 +        * \param next the param index of the next param
360 +        * \param param the parameter
361 +        */
362 +       void (*param) (void *object, int seq, uint32_t id,
363 +                      uint32_t index, uint32_t next,
364 +                      const struct spa_pod *param);
365 +};
366 +
367 +static inline void
368 +pw_endpoint_proxy_add_listener(struct pw_endpoint_proxy *p,
369 +                               struct spa_hook *listener,
370 +                               const struct pw_endpoint_proxy_events *events,
371 +                               void *data)
372 +{
373 +       pw_proxy_add_proxy_listener((struct pw_proxy*)p, listener, events, data);
374 +}
375 +
376 +#define pw_endpoint_resource_info(r,...)       \
377 +       pw_resource_notify(r,struct pw_endpoint_proxy_events,info,__VA_ARGS__)
378 +#define pw_endpoint_resource_param(r,...)      \
379 +       pw_resource_notify(r,struct pw_endpoint_proxy_events,param,__VA_ARGS__)
380 +
381 +#ifdef __cplusplus
382 +}  /* extern "C" */
383 +#endif
384 +
385 +#endif /* PIPEWIRE_EXT_ENDPOINT_H */
386 diff --git a/src/extensions/meson.build b/src/extensions/meson.build
387 index a7f5d3cb..9f690caf 100644
388 --- a/src/extensions/meson.build
389 +++ b/src/extensions/meson.build
390 @@ -1,5 +1,7 @@
391  pipewire_ext_headers = [
392 +  'client-endpoint.h',
393    'client-node.h',
394 +  'endpoint.h',
395    'protocol-native.h',
396  ]
397  
398 diff --git a/src/modules/meson.build b/src/modules/meson.build
399 index 98bc3864..572f1b6b 100644
400 --- a/src/modules/meson.build
401 +++ b/src/modules/meson.build
402 @@ -37,6 +37,18 @@ pipewire_module_client_node = shared_library('pipewire-module-client-node',
403    dependencies : [mathlib, dl_lib, pipewire_dep],
404  )
405  
406 +pipewire_module_endpoint = shared_library('pipewire-module-endpoint',
407 +  [ 'module-endpoint.c',
408 +    'module-endpoint/endpoint-impl.c',
409 +    'module-endpoint/protocol-native.c',
410 +  ],
411 +  c_args : pipewire_module_c_args,
412 +  include_directories : [configinc, spa_inc],
413 +  install : true,
414 +  install_dir : modules_install_dir,
415 +  dependencies : [mathlib, dl_lib, pipewire_dep],
416 +)
417 +
418  pipewire_module_link_factory = shared_library('pipewire-module-link-factory',
419    [ 'module-link-factory.c' ],
420    c_args : pipewire_module_c_args,
421 diff --git a/src/modules/module-endpoint.c b/src/modules/module-endpoint.c
422 new file mode 100644
423 index 00000000..d830de1b
424 --- /dev/null
425 +++ b/src/modules/module-endpoint.c
426 @@ -0,0 +1,137 @@
427 +/* PipeWire
428 + *
429 + * Copyright © 2019 Collabora Ltd.
430 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
431 + *
432 + * Permission is hereby granted, free of charge, to any person obtaining a
433 + * copy of this software and associated documentation files (the "Software"),
434 + * to deal in the Software without restriction, including without limitation
435 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
436 + * and/or sell copies of the Software, and to permit persons to whom the
437 + * Software is furnished to do so, subject to the following conditions:
438 + *
439 + * The above copyright notice and this permission notice (including the next
440 + * paragraph) shall be included in all copies or substantial portions of the
441 + * Software.
442 + *
443 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
444 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
445 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
446 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
447 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
448 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
449 + * DEALINGS IN THE SOFTWARE.
450 + */
451 +
452 +#include "config.h"
453 +
454 +#include "module-endpoint/endpoint-impl.h"
455 +
456 +struct pw_protocol *pw_protocol_native_ext_endpoint_init(struct pw_core *core);
457 +
458 +static const struct spa_dict_item module_props[] = {
459 +       { PW_MODULE_PROP_AUTHOR, "George Kiagiadakis <george.kiagiadakis@collabora.com>" },
460 +       { PW_MODULE_PROP_DESCRIPTION, "Allows clients to interract with session manager endpoints" },
461 +       { PW_MODULE_PROP_VERSION, PACKAGE_VERSION },
462 +};
463 +
464 +struct factory_data {
465 +       struct pw_factory *this;
466 +       struct pw_properties *properties;
467 +
468 +       struct pw_module *module;
469 +       struct spa_hook module_listener;
470 +};
471 +
472 +static void *create_object(void *_data,
473 +                          struct pw_resource *resource,
474 +                          uint32_t type,
475 +                          uint32_t version,
476 +                          struct pw_properties *properties,
477 +                          uint32_t new_id)
478 +{
479 +       void *result;
480 +       struct pw_resource *endpoint_resource;
481 +       struct pw_global *parent;
482 +       struct pw_client *client = pw_resource_get_client(resource);
483 +
484 +       endpoint_resource = pw_resource_new(client, new_id, PW_PERM_RWX, type, version, 0);
485 +       if (endpoint_resource == NULL)
486 +               goto no_mem;
487 +
488 +       parent = pw_client_get_global(client);
489 +
490 +       result = pw_client_endpoint_new(endpoint_resource, parent, properties);
491 +       if (result == NULL)
492 +               goto no_mem;
493 +
494 +       return result;
495 +
496 +      no_mem:
497 +       pw_log_error("can't create endpoint");
498 +       pw_resource_error(resource, -ENOMEM, "can't create endpoint: no memory");
499 +       if (properties)
500 +               pw_properties_free(properties);
501 +       return NULL;
502 +}
503 +
504 +static const struct pw_factory_implementation impl_factory = {
505 +       PW_VERSION_FACTORY_IMPLEMENTATION,
506 +       .create_object = create_object,
507 +};
508 +
509 +static void module_destroy(void *data)
510 +{
511 +       struct factory_data *d = data;
512 +
513 +       spa_hook_remove(&d->module_listener);
514 +
515 +       if (d->properties)
516 +               pw_properties_free(d->properties);
517 +
518 +       pw_factory_destroy(d->this);
519 +}
520 +
521 +static const struct pw_module_events module_events = {
522 +       PW_VERSION_MODULE_EVENTS,
523 +       .destroy = module_destroy,
524 +};
525 +
526 +static int module_init(struct pw_module *module, struct pw_properties *properties)
527 +{
528 +       struct pw_core *core = pw_module_get_core(module);
529 +       struct pw_factory *factory;
530 +       struct factory_data *data;
531 +
532 +       factory = pw_factory_new(core,
533 +                                "client-endpoint",
534 +                                PW_TYPE_INTERFACE_ClientEndpoint,
535 +                                PW_VERSION_CLIENT_ENDPOINT,
536 +                                NULL,
537 +                                sizeof(*data));
538 +       if (factory == NULL)
539 +               return -ENOMEM;
540 +
541 +       data = pw_factory_get_user_data(factory);
542 +       data->this = factory;
543 +       data->module = module;
544 +       data->properties = properties;
545 +
546 +       pw_log_debug("module-endpoint %p: new", module);
547 +
548 +       pw_factory_set_implementation(factory, &impl_factory, data);
549 +       pw_factory_register(factory, NULL, pw_module_get_global(module), NULL);
550 +
551 +       pw_protocol_native_ext_endpoint_init(core);
552 +
553 +       pw_module_add_listener(module, &data->module_listener, &module_events, data);
554 +       pw_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
555 +
556 +       return 0;
557 +}
558 +
559 +SPA_EXPORT
560 +int pipewire__module_init(struct pw_module *module, const char *args)
561 +{
562 +       return module_init(module, NULL);
563 +}
564 diff --git a/src/modules/module-endpoint/endpoint-impl.c b/src/modules/module-endpoint/endpoint-impl.c
565 new file mode 100644
566 index 00000000..252eeca1
567 --- /dev/null
568 +++ b/src/modules/module-endpoint/endpoint-impl.c
569 @@ -0,0 +1,428 @@
570 +/* PipeWire
571 + *
572 + * Copyright © 2019 Collabora Ltd.
573 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
574 + *
575 + * Permission is hereby granted, free of charge, to any person obtaining a
576 + * copy of this software and associated documentation files (the "Software"),
577 + * to deal in the Software without restriction, including without limitation
578 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
579 + * and/or sell copies of the Software, and to permit persons to whom the
580 + * Software is furnished to do so, subject to the following conditions:
581 + *
582 + * The above copyright notice and this permission notice (including the next
583 + * paragraph) shall be included in all copies or substantial portions of the
584 + * Software.
585 + *
586 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
587 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
588 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
589 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
590 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
591 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
592 + * DEALINGS IN THE SOFTWARE.
593 + */
594 +
595 +#include "endpoint-impl.h"
596 +#include <pipewire/private.h>
597 +#include <spa/pod/filter.h>
598 +#include <spa/pod/compare.h>
599 +
600 +struct pw_endpoint {
601 +       struct pw_core *core;
602 +       struct pw_global *global;
603 +       struct pw_global *parent;
604 +
605 +       struct pw_client_endpoint *client_ep;
606 +
607 +       uint32_t n_params;
608 +       struct spa_pod **params;
609 +
610 +       struct pw_endpoint_info info;
611 +       struct pw_properties *props;
612 +};
613 +
614 +struct pw_client_endpoint {
615 +       struct pw_resource *owner_resource;
616 +       struct spa_hook owner_resource_listener;
617 +
618 +       struct pw_endpoint endpoint;
619 +};
620 +
621 +struct resource_data {
622 +       struct pw_endpoint *endpoint;
623 +       struct pw_client_endpoint *client_ep;
624 +
625 +       struct spa_hook resource_listener;
626 +
627 +       uint32_t n_subscribe_ids;
628 +       uint32_t subscribe_ids[32];
629 +};
630 +
631 +static int
632 +endpoint_enum_params (void *object, int seq,
633 +                     uint32_t id, uint32_t start, uint32_t num,
634 +                     const struct spa_pod *filter)
635 +{
636 +       struct pw_resource *resource = object;
637 +       struct resource_data *data = pw_resource_get_user_data(resource);
638 +       struct pw_endpoint *this = data->endpoint;
639 +       struct spa_pod *result;
640 +       struct spa_pod *param;
641 +       uint8_t buffer[1024];
642 +       struct spa_pod_builder b = { 0 };
643 +       uint32_t index;
644 +       uint32_t next = start;
645 +       uint32_t count = 0;
646 +
647 +       while (true) {
648 +               index = next++;
649 +               if (index >= this->n_params)
650 +                       break;
651 +
652 +               param = this->params[index];
653 +
654 +               if (param == NULL || !spa_pod_is_object_id(param, id))
655 +                       continue;
656 +
657 +               spa_pod_builder_init(&b, buffer, sizeof(buffer));
658 +               if (spa_pod_filter(&b, &result, param, filter) != 0)
659 +                       continue;
660 +
661 +               pw_log_debug("endpoint %p: %d param %u", this, seq, index);
662 +
663 +               pw_endpoint_resource_param(resource, seq, id, index, next, result);
664 +
665 +               if (++count == num)
666 +                       break;
667 +       }
668 +       return 0;
669 +}
670 +
671 +static int
672 +endpoint_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids)
673 +{
674 +       struct pw_resource *resource = object;
675 +       struct resource_data *data = pw_resource_get_user_data(resource);
676 +       uint32_t i;
677 +
678 +       n_ids = SPA_MIN(n_ids, SPA_N_ELEMENTS(data->subscribe_ids));
679 +       data->n_subscribe_ids = n_ids;
680 +
681 +       for (i = 0; i < n_ids; i++) {
682 +               data->subscribe_ids[i] = ids[i];
683 +               pw_log_debug("endpoint %p: resource %d subscribe param %u",
684 +                       data->endpoint, resource->id, ids[i]);
685 +               endpoint_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL);
686 +       }
687 +       return 0;
688 +}
689 +
690 +static int
691 +endpoint_set_param (void *object, uint32_t id, uint32_t flags,
692 +                   const struct spa_pod *param)
693 +{
694 +       struct pw_resource *resource = object;
695 +       struct resource_data *data = pw_resource_get_user_data(resource);
696 +       struct pw_client_endpoint *client_ep = data->client_ep;
697 +
698 +       pw_client_endpoint_resource_set_param(client_ep->owner_resource,
699 +                                               id, flags, param);
700 +
701 +       return 0;
702 +}
703 +
704 +static const struct pw_endpoint_proxy_methods endpoint_methods = {
705 +       PW_VERSION_ENDPOINT_PROXY_METHODS,
706 +       .subscribe_params = endpoint_subscribe_params,
707 +       .enum_params = endpoint_enum_params,
708 +       .set_param = endpoint_set_param,
709 +};
710 +
711 +static void
712 +endpoint_unbind(void *data)
713 +{
714 +       struct pw_resource *resource = data;
715 +       spa_list_remove(&resource->link);
716 +}
717 +
718 +static const struct pw_resource_events resource_events = {
719 +       PW_VERSION_RESOURCE_EVENTS,
720 +       .destroy = endpoint_unbind,
721 +};
722 +
723 +static int
724 +endpoint_bind(void *_data, struct pw_client *client, uint32_t permissions,
725 +             uint32_t version, uint32_t id)
726 +{
727 +       struct pw_endpoint *this = _data;
728 +       struct pw_global *global = this->global;
729 +       struct pw_resource *resource;
730 +       struct resource_data *data;
731 +
732 +       resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data));
733 +       if (resource == NULL)
734 +               goto no_mem;
735 +
736 +       data = pw_resource_get_user_data(resource);
737 +       data->endpoint = this;
738 +       data->client_ep = this->client_ep;
739 +       pw_resource_add_listener(resource, &data->resource_listener, &resource_events, resource);
740 +
741 +       pw_resource_set_implementation(resource, &endpoint_methods, resource);
742 +
743 +       pw_log_debug("endpoint %p: bound to %d", this, resource->id);
744 +
745 +       spa_list_append(&global->resource_list, &resource->link);
746 +
747 +       this->info.change_mask = PW_ENDPOINT_CHANGE_MASK_PARAMS |
748 +                                PW_ENDPOINT_CHANGE_MASK_PROPS;
749 +       pw_endpoint_resource_info(resource, &this->info);
750 +       this->info.change_mask = 0;
751 +
752 +       return 0;
753 +
754 +      no_mem:
755 +       pw_log_error("can't create node resource");
756 +       return -ENOMEM;
757 +}
758 +
759 +static int
760 +pw_endpoint_init(struct pw_endpoint *this,
761 +                struct pw_core *core,
762 +                struct pw_client *owner,
763 +                struct pw_global *parent,
764 +                struct pw_properties *properties)
765 +{
766 +       struct pw_properties *props = NULL;
767 +
768 +       pw_log_debug("endpoint %p: new", this);
769 +
770 +       this->core = core;
771 +       this->parent = parent;
772 +
773 +       props = properties ? properties : pw_properties_new(NULL, NULL);
774 +       if (!props)
775 +               goto no_mem;
776 +
777 +       this->props = pw_properties_copy (props);
778 +       if (!this->props)
779 +               goto no_mem;
780 +
781 +       this->global = pw_global_new (core,
782 +                       PW_TYPE_INTERFACE_Endpoint,
783 +                       PW_VERSION_ENDPOINT,
784 +                       props, endpoint_bind, this);
785 +       if (!this->global)
786 +               goto no_mem;
787 +
788 +       this->info.id = this->global->id;
789 +       this->info.props = &this->props->dict;
790 +
791 +       return pw_global_register(this->global, owner, parent);
792 +
793 +      no_mem:
794 +       pw_log_error("can't create endpoint - out of memory");
795 +       if (props && !properties)
796 +               pw_properties_free(props);
797 +       if (this->props)
798 +               pw_properties_free(this->props);
799 +       return -ENOMEM;
800 +}
801 +
802 +static void
803 +pw_endpoint_clear(struct pw_endpoint *this)
804 +{
805 +       uint32_t i;
806 +
807 +       pw_log_debug("endpoint %p: destroy", this);
808 +
809 +       pw_global_destroy(this->global);
810 +
811 +       for (i = 0; i < this->n_params; i++)
812 +               free(this->params[i]);
813 +       free(this->params);
814 +
815 +       free(this->info.params);
816 +
817 +       if (this->props)
818 +               pw_properties_free(this->props);
819 +}
820 +
821 +static void
822 +endpoint_notify_subscribed(struct pw_endpoint *this,
823 +                          uint32_t index, uint32_t next)
824 +{
825 +       struct pw_global *global = this->global;
826 +       struct pw_resource *resource;
827 +       struct resource_data *data;
828 +       struct spa_pod *param = this->params[index];
829 +       uint32_t id;
830 +       uint32_t i;
831 +
832 +       if (!param || !spa_pod_is_object (param))
833 +               return;
834 +
835 +       id = SPA_POD_OBJECT_ID (param);
836 +
837 +       spa_list_for_each(resource, &global->resource_list, link) {
838 +               data = pw_resource_get_user_data(resource);
839 +               for (i = 0; i < data->n_subscribe_ids; i++) {
840 +                       if (data->subscribe_ids[i] == id) {
841 +                               pw_endpoint_resource_param(resource, 1, id,
842 +                                       index, next, param);
843 +                       }
844 +               }
845 +       }
846 +}
847 +
848 +static int
849 +client_endpoint_update(void *object,
850 +       uint32_t change_mask,
851 +       uint32_t n_params,
852 +       const struct spa_pod **params,
853 +       const struct pw_endpoint_info *info)
854 +{
855 +       struct pw_client_endpoint *cliep = object;
856 +       struct pw_endpoint *this = &cliep->endpoint;
857 +
858 +       if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS) {
859 +               uint32_t i;
860 +
861 +               pw_log_debug("endpoint %p: update %d params", this, n_params);
862 +
863 +               for (i = 0; i < this->n_params; i++)
864 +                       free(this->params[i]);
865 +               this->n_params = n_params;
866 +               this->params = realloc(this->params, this->n_params * sizeof(struct spa_pod *));
867 +
868 +               for (i = 0; i < this->n_params; i++) {
869 +                       this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL;
870 +                       endpoint_notify_subscribed(this, i, i+1);
871 +               }
872 +       }
873 +       else if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS_INCREMENTAL) {
874 +               uint32_t i, j;
875 +               const struct spa_pod_prop *pold, *pnew;
876 +
877 +               pw_log_debug("endpoint %p: update %d params incremental", this, n_params);
878 +
879 +               for (i = 0; i < this->n_params; i++) {
880 +                       /* we only support incremental updates for controls */
881 +                       if (!spa_pod_is_object_id (this->params[i], PW_ENDPOINT_PARAM_Control))
882 +                               continue;
883 +
884 +                       for (j = 0; j < n_params; j++) {
885 +                               if (!spa_pod_is_object_id (params[j], PW_ENDPOINT_PARAM_Control)) {
886 +                                       pw_log_warn ("endpoint %p: ignoring incremental update "
887 +                                               "on non-control param", this);
888 +                                       continue;
889 +                               }
890 +
891 +                               pold = spa_pod_object_find_prop (
892 +                                       (const struct spa_pod_object *) this->params[i],
893 +                                       NULL, PW_ENDPOINT_PARAM_CONTROL_id);
894 +                               pnew = spa_pod_object_find_prop (
895 +                                       (const struct spa_pod_object *) params[j],
896 +                                       NULL, PW_ENDPOINT_PARAM_CONTROL_id);
897 +
898 +                               if (pold && pnew && spa_pod_compare (&pold->value, &pnew->value) == 0) {
899 +                                       free (this->params[i]);
900 +                                       this->params[i] = spa_pod_copy (params[j]);
901 +                                       endpoint_notify_subscribed(this, i, UINT32_MAX);
902 +                               }
903 +                       }
904 +               }
905 +       }
906 +
907 +       if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO) {
908 +               struct pw_global *global = this->global;
909 +               struct pw_resource *resource;
910 +
911 +               if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) {
912 +                       size_t size = info->n_params * sizeof(struct spa_param_info);
913 +                       free(this->info.params);
914 +                       this->info.params = malloc(size);
915 +                       this->info.n_params = info->n_params;
916 +                       memcpy(this->info.params, info->params, size);
917 +               }
918 +
919 +               if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) {
920 +                       pw_properties_update(this->props, info->props);
921 +               }
922 +
923 +               this->info.change_mask = info->change_mask;
924 +               spa_list_for_each(resource, &global->resource_list, link) {
925 +                       pw_endpoint_resource_info(resource, &this->info);
926 +               }
927 +               this->info.change_mask = 0;
928 +       }
929 +
930 +       return 0;
931 +}
932 +
933 +static struct pw_client_endpoint_proxy_methods client_endpoint_methods = {
934 +       PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS,
935 +       .update = client_endpoint_update,
936 +};
937 +
938 +static void
939 +client_endpoint_resource_destroy(void *data)
940 +{
941 +       struct pw_client_endpoint *this = data;
942 +
943 +       pw_log_debug("client-endpoint %p: destroy", this);
944 +
945 +       pw_endpoint_clear(&this->endpoint);
946 +
947 +       this->owner_resource = NULL;
948 +       spa_hook_remove(&this->owner_resource_listener);
949 +       free(this);
950 +}
951 +
952 +static const struct pw_resource_events owner_resource_events = {
953 +       PW_VERSION_RESOURCE_EVENTS,
954 +       .destroy = client_endpoint_resource_destroy,
955 +};
956 +
957 +struct pw_client_endpoint *
958 +pw_client_endpoint_new(struct pw_resource *owner_resource,
959 +                       struct pw_global *parent,
960 +                       struct pw_properties *properties)
961 +{
962 +       struct pw_client_endpoint *this;
963 +       struct pw_client *owner = pw_resource_get_client(owner_resource);
964 +       struct pw_core *core = pw_client_get_core(owner);
965 +
966 +       this = calloc(1, sizeof(struct pw_client_endpoint));
967 +       if (this == NULL)
968 +               return NULL;
969 +
970 +       pw_log_debug("client-endpoint %p: new", this);
971 +
972 +       if (pw_endpoint_init(&this->endpoint, core, owner, parent, properties) < 0)
973 +               goto error_no_endpoint;
974 +       this->endpoint.client_ep = this;
975 +
976 +       this->owner_resource = owner_resource;
977 +       pw_resource_add_listener(this->owner_resource,
978 +                                &this->owner_resource_listener,
979 +                                &owner_resource_events,
980 +                                this);
981 +       pw_resource_set_implementation(this->owner_resource,
982 +                                      &client_endpoint_methods,
983 +                                      this);
984 +
985 +       return this;
986 +
987 +      error_no_endpoint:
988 +       pw_resource_destroy(owner_resource);
989 +       free(this);
990 +       return NULL;
991 +}
992 +
993 +void
994 +pw_client_endpoint_destroy(struct pw_client_endpoint *this)
995 +{
996 +       pw_resource_destroy(this->owner_resource);
997 +}
998 diff --git a/src/modules/module-endpoint/endpoint-impl.h b/src/modules/module-endpoint/endpoint-impl.h
999 new file mode 100644
1000 index 00000000..059aa904
1001 --- /dev/null
1002 +++ b/src/modules/module-endpoint/endpoint-impl.h
1003 @@ -0,0 +1,52 @@
1004 +/* PipeWire
1005 + *
1006 + * Copyright © 2019 Collabora Ltd.
1007 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
1008 + *
1009 + * Permission is hereby granted, free of charge, to any person obtaining a
1010 + * copy of this software and associated documentation files (the "Software"),
1011 + * to deal in the Software without restriction, including without limitation
1012 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1013 + * and/or sell copies of the Software, and to permit persons to whom the
1014 + * Software is furnished to do so, subject to the following conditions:
1015 + *
1016 + * The above copyright notice and this permission notice (including the next
1017 + * paragraph) shall be included in all copies or substantial portions of the
1018 + * Software.
1019 + *
1020 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1021 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1022 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1023 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1024 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1025 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1026 + * DEALINGS IN THE SOFTWARE.
1027 + */
1028 +
1029 +#ifndef PIPEWIRE_ENDPOINT_IMPL_H
1030 +#define PIPEWIRE_ENDPOINT_IMPL_H
1031 +
1032 +#include <pipewire/pipewire.h>
1033 +#include <extensions/endpoint.h>
1034 +#include <extensions/client-endpoint.h>
1035 +
1036 +#ifdef __cplusplus
1037 +extern "C" {
1038 +#endif
1039 +
1040 +struct pw_endpoint;
1041 +struct pw_client_endpoint;
1042 +
1043 +struct pw_client_endpoint *
1044 +pw_client_endpoint_new(struct pw_resource *resource,
1045 +                       struct pw_global *parent,
1046 +                       struct pw_properties *properties);
1047 +
1048 +void
1049 +pw_client_endpoint_destroy(struct pw_client_endpoint *endpoint);
1050 +
1051 +#ifdef __cplusplus
1052 +}
1053 +#endif
1054 +
1055 +#endif /* PIPEWIRE_ENDPOINT_IMPL_H */
1056 diff --git a/src/modules/module-endpoint/protocol-native.c b/src/modules/module-endpoint/protocol-native.c
1057 new file mode 100644
1058 index 00000000..a41d3119
1059 --- /dev/null
1060 +++ b/src/modules/module-endpoint/protocol-native.c
1061 @@ -0,0 +1,472 @@
1062 +/* PipeWire
1063 + *
1064 + * Copyright © 2019 Collabora Ltd.
1065 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
1066 + *
1067 + * Permission is hereby granted, free of charge, to any person obtaining a
1068 + * copy of this software and associated documentation files (the "Software"),
1069 + * to deal in the Software without restriction, including without limitation
1070 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1071 + * and/or sell copies of the Software, and to permit persons to whom the
1072 + * Software is furnished to do so, subject to the following conditions:
1073 + *
1074 + * The above copyright notice and this permission notice (including the next
1075 + * paragraph) shall be included in all copies or substantial portions of the
1076 + * Software.
1077 + *
1078 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1079 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1080 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1081 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1082 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1083 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1084 + * DEALINGS IN THE SOFTWARE.
1085 + */
1086 +
1087 +#include <pipewire/pipewire.h>
1088 +#include <spa/pod/parser.h>
1089 +
1090 +#include <extensions/client-endpoint.h>
1091 +#include <extensions/endpoint.h>
1092 +#include <extensions/protocol-native.h>
1093 +
1094 +static void
1095 +serialize_pw_endpoint_info(struct spa_pod_builder *b,
1096 +                          const struct pw_endpoint_info *info)
1097 +{
1098 +       struct spa_pod_frame f;
1099 +       uint32_t i, n_props;
1100 +
1101 +       n_props = info->props ? info->props->n_items : 0;
1102 +
1103 +       spa_pod_builder_push_struct(b, &f);
1104 +       spa_pod_builder_add(b,
1105 +               SPA_POD_Id(info->id),
1106 +               SPA_POD_Int(info->change_mask),
1107 +               SPA_POD_Int(info->n_params),
1108 +               SPA_POD_Int(n_props),
1109 +               NULL);
1110 +
1111 +       for (i = 0; i < info->n_params; i++) {
1112 +               spa_pod_builder_add(b,
1113 +                       SPA_POD_Id(info->params[i].id),
1114 +                       SPA_POD_Int(info->params[i].flags), NULL);
1115 +       }
1116 +
1117 +       for (i = 0; i < n_props; i++) {
1118 +               spa_pod_builder_add(b,
1119 +                       SPA_POD_String(info->props->items[i].key),
1120 +                       SPA_POD_String(info->props->items[i].value),
1121 +                       NULL);
1122 +       }
1123 +
1124 +       spa_pod_builder_pop(b, &f);
1125 +}
1126 +
1127 +/* macro because of alloca() */
1128 +#define deserialize_pw_endpoint_info(p, f, info) \
1129 +do { \
1130 +       if (spa_pod_parser_push_struct(p, f) < 0 || \
1131 +           spa_pod_parser_get(p, \
1132 +                       SPA_POD_Id(&(info)->id), \
1133 +                       SPA_POD_Int(&(info)->change_mask), \
1134 +                       SPA_POD_Int(&(info)->n_params), \
1135 +                       SPA_POD_Int(&(info)->props->n_items), \
1136 +                       NULL) < 0) \
1137 +               return -EINVAL; \
1138 +       \
1139 +       if ((info)->n_params > 0) \
1140 +               (info)->params = alloca((info)->n_params * sizeof(struct spa_param_info)); \
1141 +       if ((info)->props->n_items > 0) \
1142 +               (info)->props->items = alloca((info)->props->n_items * sizeof(struct spa_dict_item)); \
1143 +       \
1144 +       for (i = 0; i < (info)->n_params; i++) { \
1145 +               if (spa_pod_parser_get(p, \
1146 +                               SPA_POD_Id(&(info)->params[i].id), \
1147 +                               SPA_POD_Int(&(info)->params[i].flags), \
1148 +                               NULL) < 0) \
1149 +                       return -EINVAL; \
1150 +       } \
1151 +       \
1152 +       for (i = 0; i < (info)->props->n_items; i++) { \
1153 +               if (spa_pod_parser_get(p, \
1154 +                               SPA_POD_String(&(info)->props->items[i].key), \
1155 +                               SPA_POD_String(&(info)->props->items[i].value), \
1156 +                               NULL) < 0) \
1157 +                       return -EINVAL; \
1158 +       } \
1159 +       \
1160 +       spa_pod_parser_pop(p, f); \
1161 +} while(0)
1162 +
1163 +static int
1164 +endpoint_marshal_subscribe_params(void *object, uint32_t *ids, uint32_t n_ids)
1165 +{
1166 +       struct pw_proxy *proxy = object;
1167 +       struct spa_pod_builder *b;
1168 +
1169 +       b = pw_protocol_native_begin_proxy(proxy,
1170 +               PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS, NULL);
1171 +
1172 +       spa_pod_builder_add_struct(b,
1173 +                       SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, n_ids, ids));
1174 +
1175 +       return pw_protocol_native_end_proxy(proxy, b);
1176 +}
1177 +
1178 +static int
1179 +endpoint_demarshal_subscribe_params(void *object, const struct pw_protocol_native_message *msg)
1180 +{
1181 +       struct pw_resource *resource = object;
1182 +       struct spa_pod_parser prs;
1183 +       uint32_t csize, ctype, n_ids;
1184 +       uint32_t *ids;
1185 +
1186 +       spa_pod_parser_init(&prs, msg->data, msg->size);
1187 +       if (spa_pod_parser_get_struct(&prs,
1188 +                               SPA_POD_Array(&csize, &ctype, &n_ids, &ids)) < 0)
1189 +               return -EINVAL;
1190 +
1191 +       if (ctype != SPA_TYPE_Id)
1192 +               return -EINVAL;
1193 +
1194 +       return pw_resource_do(resource, struct pw_endpoint_proxy_methods,
1195 +                       subscribe_params, 0, ids, n_ids);
1196 +}
1197 +
1198 +static int
1199 +endpoint_marshal_enum_params(void *object, int seq, uint32_t id,
1200 +               uint32_t index, uint32_t num, const struct spa_pod *filter)
1201 +{
1202 +       struct pw_proxy *proxy = object;
1203 +       struct spa_pod_builder *b;
1204 +
1205 +       b = pw_protocol_native_begin_proxy(proxy,
1206 +               PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS, NULL);
1207 +
1208 +       spa_pod_builder_add_struct(b,
1209 +                       SPA_POD_Int(seq),
1210 +                       SPA_POD_Id(id),
1211 +                       SPA_POD_Int(index),
1212 +                       SPA_POD_Int(num),
1213 +                       SPA_POD_Pod(filter));
1214 +
1215 +       return pw_protocol_native_end_proxy(proxy, b);
1216 +}
1217 +
1218 +static int
1219 +endpoint_demarshal_enum_params(void *object, const struct pw_protocol_native_message *msg)
1220 +{
1221 +       struct pw_resource *resource = object;
1222 +       struct spa_pod_parser prs;
1223 +       uint32_t id, index, num;
1224 +       int seq;
1225 +       struct spa_pod *filter;
1226 +
1227 +       spa_pod_parser_init(&prs, msg->data, msg->size);
1228 +       if (spa_pod_parser_get_struct(&prs,
1229 +                               SPA_POD_Int(&seq),
1230 +                               SPA_POD_Id(&id),
1231 +                               SPA_POD_Int(&index),
1232 +                               SPA_POD_Int(&num),
1233 +                               SPA_POD_Pod(&filter)) < 0)
1234 +               return -EINVAL;
1235 +
1236 +       return pw_resource_do(resource, struct pw_endpoint_proxy_methods,
1237 +                       enum_params, 0, seq, id, index, num, filter);
1238 +}
1239 +
1240 +static int
1241 +endpoint_marshal_set_param(void *object, uint32_t id, uint32_t flags,
1242 +               const struct spa_pod *param)
1243 +{
1244 +       struct pw_proxy *proxy = object;
1245 +       struct spa_pod_builder *b;
1246 +
1247 +       b = pw_protocol_native_begin_proxy(proxy,
1248 +               PW_ENDPOINT_PROXY_METHOD_SET_PARAM, NULL);
1249 +
1250 +       spa_pod_builder_add_struct(b,
1251 +                       SPA_POD_Id(id),
1252 +                       SPA_POD_Int(flags),
1253 +                       SPA_POD_Pod(param));
1254 +       return pw_protocol_native_end_proxy(proxy, b);
1255 +}
1256 +
1257 +static int
1258 +endpoint_demarshal_set_param(void *object, const struct pw_protocol_native_message *msg)
1259 +{
1260 +       struct pw_resource *resource = object;
1261 +       struct spa_pod_parser prs;
1262 +       uint32_t id, flags;
1263 +       struct spa_pod *param;
1264 +
1265 +       spa_pod_parser_init(&prs, msg->data, msg->size);
1266 +       if (spa_pod_parser_get_struct(&prs,
1267 +                               SPA_POD_Id(&id),
1268 +                               SPA_POD_Int(&flags),
1269 +                               SPA_POD_Pod(&param)) < 0)
1270 +               return -EINVAL;
1271 +
1272 +       return pw_resource_do(resource, struct pw_endpoint_proxy_methods,
1273 +                       set_param, 0, id, flags, param);
1274 +}
1275 +
1276 +static void
1277 +endpoint_marshal_info(void *object, const struct pw_endpoint_info *info)
1278 +{
1279 +       struct pw_resource *resource = object;
1280 +       struct spa_pod_builder *b;
1281 +
1282 +       b = pw_protocol_native_begin_resource(resource,
1283 +                       PW_ENDPOINT_PROXY_EVENT_INFO, NULL);
1284 +       serialize_pw_endpoint_info (b, info);
1285 +       pw_protocol_native_end_resource(resource, b);
1286 +}
1287 +
1288 +static int
1289 +endpoint_demarshal_info(void *object, const struct pw_protocol_native_message *msg)
1290 +{
1291 +       struct pw_proxy *proxy = object;
1292 +       struct spa_pod_parser prs;
1293 +       struct spa_pod_frame f;
1294 +       struct spa_dict props = SPA_DICT_INIT(NULL, 0);
1295 +       struct pw_endpoint_info info = { .props = &props };
1296 +       uint32_t i;
1297 +
1298 +       spa_pod_parser_init(&prs, msg->data, msg->size);
1299 +
1300 +       deserialize_pw_endpoint_info(&prs, &f, &info);
1301 +
1302 +       return pw_proxy_notify(proxy, struct pw_endpoint_proxy_events,
1303 +               info, 0, &info);
1304 +}
1305 +
1306 +static void
1307 +endpoint_marshal_param(void *object, int seq, uint32_t id,
1308 +               uint32_t index, uint32_t next, const struct spa_pod *param)
1309 +{
1310 +       struct pw_resource *resource = object;
1311 +       struct spa_pod_builder *b;
1312 +
1313 +       b = pw_protocol_native_begin_resource(resource,
1314 +                       PW_ENDPOINT_PROXY_EVENT_PARAM, NULL);
1315 +
1316 +       spa_pod_builder_add_struct(b,
1317 +                       SPA_POD_Int(seq),
1318 +                       SPA_POD_Id(id),
1319 +                       SPA_POD_Int(index),
1320 +                       SPA_POD_Int(next),
1321 +                       SPA_POD_Pod(param));
1322 +
1323 +       pw_protocol_native_end_resource(resource, b);
1324 +}
1325 +
1326 +static int
1327 +endpoint_demarshal_param(void *object, const struct pw_protocol_native_message *msg)
1328 +{
1329 +       struct pw_proxy *proxy = object;
1330 +       struct spa_pod_parser prs;
1331 +       uint32_t id, index, next;
1332 +       int seq;
1333 +       struct spa_pod *param;
1334 +
1335 +       spa_pod_parser_init(&prs, msg->data, msg->size);
1336 +       if (spa_pod_parser_get_struct(&prs,
1337 +                               SPA_POD_Int(&seq),
1338 +                               SPA_POD_Id(&id),
1339 +                               SPA_POD_Int(&index),
1340 +                               SPA_POD_Int(&next),
1341 +                               SPA_POD_Pod(&param)) < 0)
1342 +               return -EINVAL;
1343 +
1344 +       return pw_proxy_notify(proxy, struct pw_endpoint_proxy_events, param, 0,
1345 +                       seq, id, index, next, param);
1346 +}
1347 +
1348 +static const struct pw_endpoint_proxy_methods pw_protocol_native_endpoint_method_marshal = {
1349 +       PW_VERSION_ENDPOINT_PROXY_METHODS,
1350 +       &endpoint_marshal_subscribe_params,
1351 +       &endpoint_marshal_enum_params,
1352 +       &endpoint_marshal_set_param,
1353 +};
1354 +
1355 +static const struct pw_protocol_native_demarshal pw_protocol_native_endpoint_method_demarshal[] = {
1356 +       { &endpoint_demarshal_subscribe_params, 0 },
1357 +       { &endpoint_demarshal_enum_params, 0 },
1358 +       { &endpoint_demarshal_set_param, 0 }
1359 +};
1360 +
1361 +static const struct pw_endpoint_proxy_events pw_protocol_native_endpoint_event_marshal = {
1362 +       PW_VERSION_ENDPOINT_PROXY_EVENTS,
1363 +       &endpoint_marshal_info,
1364 +       &endpoint_marshal_param,
1365 +};
1366 +
1367 +static const struct pw_protocol_native_demarshal pw_protocol_native_endpoint_event_demarshal[] = {
1368 +       { &endpoint_demarshal_info, 0 },
1369 +       { &endpoint_demarshal_param, 0 }
1370 +};
1371 +
1372 +static const struct pw_protocol_marshal pw_protocol_native_endpoint_marshal = {
1373 +       PW_TYPE_INTERFACE_Endpoint,
1374 +       PW_VERSION_ENDPOINT,
1375 +       PW_ENDPOINT_PROXY_METHOD_NUM,
1376 +       PW_ENDPOINT_PROXY_EVENT_NUM,
1377 +       &pw_protocol_native_endpoint_method_marshal,
1378 +       &pw_protocol_native_endpoint_method_demarshal,
1379 +       &pw_protocol_native_endpoint_event_marshal,
1380 +       &pw_protocol_native_endpoint_event_demarshal,
1381 +};
1382 +
1383 +
1384 +static int
1385 +client_endpoint_marshal_update(
1386 +       void *object,
1387 +       uint32_t change_mask,
1388 +       uint32_t n_params,
1389 +       const struct spa_pod **params,
1390 +       const struct pw_endpoint_info *info)
1391 +{
1392 +       struct pw_proxy *proxy = object;
1393 +       struct spa_pod_builder *b;
1394 +       struct spa_pod_frame f;
1395 +       uint32_t i;
1396 +
1397 +       b = pw_protocol_native_begin_proxy(proxy,
1398 +               PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE, NULL);
1399 +
1400 +       spa_pod_builder_push_struct(b, &f);
1401 +       spa_pod_builder_add(b,
1402 +               SPA_POD_Int(change_mask),
1403 +               SPA_POD_Int(n_params), NULL);
1404 +
1405 +       for (i = 0; i < n_params; i++)
1406 +               spa_pod_builder_add(b, SPA_POD_Pod(params[i]), NULL);
1407 +
1408 +       if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO)
1409 +               serialize_pw_endpoint_info(b, info);
1410 +
1411 +       spa_pod_builder_pop(b, &f);
1412 +
1413 +       return pw_protocol_native_end_proxy(proxy, b);
1414 +}
1415 +
1416 +static int
1417 +client_endpoint_demarshal_update(void *object,
1418 +       const struct pw_protocol_native_message *msg)
1419 +{
1420 +       struct pw_resource *resource = object;
1421 +       struct spa_pod_parser prs;
1422 +       struct spa_pod_frame f[2];
1423 +       uint32_t change_mask, n_params;
1424 +       const struct spa_pod **params = NULL;
1425 +       struct spa_dict props = SPA_DICT_INIT(NULL, 0);
1426 +       struct pw_endpoint_info info = { .props = &props };
1427 +       uint32_t i;
1428 +
1429 +       spa_pod_parser_init(&prs, msg->data, msg->size);
1430 +       if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 ||
1431 +           spa_pod_parser_get(&prs,
1432 +                       SPA_POD_Int(&change_mask),
1433 +                       SPA_POD_Int(&n_params), NULL) < 0)
1434 +               return -EINVAL;
1435 +
1436 +       if (n_params > 0)
1437 +               params = alloca(n_params * sizeof(struct spa_pod *));
1438 +       for (i = 0; i < n_params; i++)
1439 +               if (spa_pod_parser_get(&prs,
1440 +                               SPA_POD_PodObject(&params[i]), NULL) < 0)
1441 +                       return -EINVAL;
1442 +
1443 +       if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO)
1444 +               deserialize_pw_endpoint_info(&prs, &f[1], &info);
1445 +
1446 +       pw_resource_do(resource, struct pw_client_endpoint_proxy_methods,
1447 +               update, 0, change_mask, n_params, params, &info);
1448 +       return 0;
1449 +}
1450 +
1451 +static void
1452 +client_endpoint_marshal_set_param (void *object,
1453 +       uint32_t id, uint32_t flags,
1454 +       const struct spa_pod *param)
1455 +{
1456 +       struct pw_resource *resource = object;
1457 +       struct spa_pod_builder *b;
1458 +
1459 +       b = pw_protocol_native_begin_resource(resource,
1460 +               PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM, NULL);
1461 +
1462 +       spa_pod_builder_add_struct(b,
1463 +                               SPA_POD_Id(id),
1464 +                               SPA_POD_Int(flags),
1465 +                               SPA_POD_Pod(param));
1466 +
1467 +       pw_protocol_native_end_resource(resource, b);
1468 +}
1469 +
1470 +static int
1471 +client_endpoint_demarshal_set_param(void *object,
1472 +       const struct pw_protocol_native_message *msg)
1473 +{
1474 +       struct pw_proxy *proxy = object;
1475 +       struct spa_pod_parser prs;
1476 +       uint32_t id, flags;
1477 +       const struct spa_pod *param = NULL;
1478 +
1479 +       spa_pod_parser_init(&prs, msg->data, msg->size);
1480 +       if (spa_pod_parser_get_struct(&prs,
1481 +                       SPA_POD_Id(&id),
1482 +                       SPA_POD_Int(&flags),
1483 +                       SPA_POD_PodObject(&param)) < 0)
1484 +               return -EINVAL;
1485 +
1486 +       pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events,
1487 +                       set_param, 0, id, flags, param);
1488 +       return 0;
1489 +}
1490 +
1491 +static const struct pw_client_endpoint_proxy_methods pw_protocol_native_client_endpoint_method_marshal = {
1492 +       PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS,
1493 +       &client_endpoint_marshal_update,
1494 +};
1495 +
1496 +static const struct pw_protocol_native_demarshal pw_protocol_native_client_endpoint_method_demarshal[] = {
1497 +       { &client_endpoint_demarshal_update, 0 }
1498 +};
1499 +
1500 +static const struct pw_client_endpoint_proxy_events pw_protocol_native_client_endpoint_event_marshal = {
1501 +       PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS,
1502 +       &client_endpoint_marshal_set_param,
1503 +};
1504 +
1505 +static const struct pw_protocol_native_demarshal pw_protocol_native_client_endpoint_event_demarshal[] = {
1506 +       { &client_endpoint_demarshal_set_param, 0 }
1507 +};
1508 +
1509 +static const struct pw_protocol_marshal pw_protocol_native_client_endpoint_marshal = {
1510 +       PW_TYPE_INTERFACE_ClientEndpoint,
1511 +       PW_VERSION_CLIENT_ENDPOINT,
1512 +       PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM,
1513 +       PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM,
1514 +       &pw_protocol_native_client_endpoint_method_marshal,
1515 +       &pw_protocol_native_client_endpoint_method_demarshal,
1516 +       &pw_protocol_native_client_endpoint_event_marshal,
1517 +       &pw_protocol_native_client_endpoint_event_demarshal,
1518 +};
1519 +
1520 +struct pw_protocol *pw_protocol_native_ext_endpoint_init(struct pw_core *core)
1521 +{
1522 +       struct pw_protocol *protocol;
1523 +
1524 +       protocol = pw_core_find_protocol(core, PW_TYPE_INFO_PROTOCOL_Native);
1525 +
1526 +       if (protocol == NULL)
1527 +               return NULL;
1528 +
1529 +       pw_protocol_add_marshal(protocol, &pw_protocol_native_client_endpoint_marshal);
1530 +       pw_protocol_add_marshal(protocol, &pw_protocol_native_endpoint_marshal);
1531 +
1532 +       return protocol;
1533 +}
1534 diff --git a/src/pipewire/pipewire.c b/src/pipewire/pipewire.c
1535 index a8752438..bbbf9420 100644
1536 --- a/src/pipewire/pipewire.c
1537 +++ b/src/pipewire/pipewire.c
1538 @@ -647,6 +647,8 @@ static const struct spa_type_info type_info[] = {
1539         { PW_TYPE_INTERFACE_Module, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Module", NULL },
1540         { PW_TYPE_INTERFACE_ClientNode, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientNode", NULL },
1541         { PW_TYPE_INTERFACE_Device, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Device", NULL },
1542 +       { PW_TYPE_INTERFACE_Endpoint, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Endpoint", NULL},
1543 +       { PW_TYPE_INTERFACE_ClientEndpoint, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientEndpoint", NULL},
1544         { SPA_ID_INVALID, SPA_ID_INVALID, "spa_types", spa_types },
1545         { 0, 0, NULL, NULL },
1546  };
1547 diff --git a/src/pipewire/type.h b/src/pipewire/type.h
1548 index a1b205f7..39544913 100644
1549 --- a/src/pipewire/type.h
1550 +++ b/src/pipewire/type.h
1551 @@ -48,7 +48,8 @@ enum {
1552         /* extensions */
1553         PW_TYPE_INTERFACE_EXTENSIONS = PW_TYPE_INTERFACE_START + 0x1000,
1554         PW_TYPE_INTERFACE_ClientNode,
1555 -
1556 +       PW_TYPE_INTERFACE_Endpoint,
1557 +       PW_TYPE_INTERFACE_ClientEndpoint,
1558  };
1559  
1560  #define PW_TYPE_INFO_BASE              "PipeWire:"
1561 -- 
1562 2.20.1
1563