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