blob: b194f4c64cb4a091b16d1a8ae3b2155415ade86f [file] [log] [blame]
Michael Olbrichd5d5aa92019-04-23 12:34:05 +02001/*
2 * Copyright © 2019 Pengutronix, Michael Olbrich <m.olbrich@pengutronix.de>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
Marius Vladbb7ed372020-11-05 19:30:51 +020026#include "config.h"
27
Marius Vladc4076ef2020-08-28 16:59:36 +030028#include <libweston/pipewire-plugin.h>
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020029#include "backend.h"
30#include "libweston-internal.h"
31#include "shared/timespec-util.h"
32#include <libweston/backend-drm.h>
33#include <libweston/weston-log.h>
34
35#include <sys/mman.h>
36#include <errno.h>
37#include <unistd.h>
38
James Hilliard80b585f2020-07-06 00:58:02 -060039#include <pipewire/pipewire.h>
40
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020041#include <spa/param/format-utils.h>
42#include <spa/param/video/format-utils.h>
43#include <spa/utils/defs.h>
44
James Hilliard80b585f2020-07-06 00:58:02 -060045#include <spa/buffer/meta.h>
46#include <spa/utils/result.h>
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020047
48#define PROP_RANGE(min, max) 2, (min), (max)
49
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020050struct weston_pipewire {
51 struct weston_compositor *compositor;
52 struct wl_list output_list;
53 struct wl_listener destroy_listener;
54 const struct weston_drm_virtual_output_api *virtual_output_api;
55
56 struct weston_log_scope *debug;
57
58 struct pw_loop *loop;
59 struct wl_event_source *loop_source;
60
James Hilliard80b585f2020-07-06 00:58:02 -060061 struct pw_context *context;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020062 struct pw_core *core;
63 struct pw_type *t;
James Hilliard80b585f2020-07-06 00:58:02 -060064 struct spa_hook core_listener;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020065};
66
67struct pipewire_output {
68 struct weston_output *output;
69 void (*saved_destroy)(struct weston_output *output);
70 int (*saved_enable)(struct weston_output *output);
71 int (*saved_disable)(struct weston_output *output);
72 int (*saved_start_repaint_loop)(struct weston_output *output);
73
74 struct weston_head *head;
75
76 struct weston_pipewire *pipewire;
77
78 uint32_t seq;
79 struct pw_stream *stream;
80 struct spa_hook stream_listener;
81
82 struct spa_video_info_raw video_format;
83
84 struct wl_event_source *finish_frame_timer;
85 struct wl_list link;
86 bool submitted_frame;
Michael Olbricha24a3262020-08-19 08:43:15 +020087 enum dpms_enum dpms;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020088};
89
90struct pipewire_frame_data {
91 struct pipewire_output *output;
92 int fd;
93 int stride;
94 struct drm_fb *drm_buffer;
95 int fence_sync_fd;
96 struct wl_event_source *fence_sync_event_source;
97};
98
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020099static void
100pipewire_debug_impl(struct weston_pipewire *pipewire,
101 struct pipewire_output *output,
102 const char *fmt, va_list ap)
103{
104 FILE *fp;
105 char *logstr;
106 size_t logsize;
107 char timestr[128];
108
109 if (!weston_log_scope_is_enabled(pipewire->debug))
110 return;
111
112 fp = open_memstream(&logstr, &logsize);
113 if (!fp)
114 return;
115
116 weston_log_scope_timestamp(pipewire->debug, timestr, sizeof timestr);
117 fprintf(fp, "%s", timestr);
118
119 if (output)
120 fprintf(fp, "[%s]", output->output->name);
121
122 fprintf(fp, " ");
123 vfprintf(fp, fmt, ap);
124 fprintf(fp, "\n");
125
126 if (fclose(fp) == 0)
127 weston_log_scope_write(pipewire->debug, logstr, logsize);
128
129 free(logstr);
130}
131
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200132static void
133pipewire_output_debug(struct pipewire_output *output, const char *fmt, ...)
134{
135 va_list ap;
136
137 va_start(ap, fmt);
138 pipewire_debug_impl(output->pipewire, output, fmt, ap);
139 va_end(ap);
140}
141
142static struct weston_pipewire *
143weston_pipewire_get(struct weston_compositor *compositor);
144
145static struct pipewire_output *
146lookup_pipewire_output(struct weston_output *base_output)
147{
148 struct weston_compositor *c = base_output->compositor;
149 struct weston_pipewire *pipewire = weston_pipewire_get(c);
150 struct pipewire_output *output;
151
152 wl_list_for_each(output, &pipewire->output_list, link) {
153 if (output->output == base_output)
154 return output;
155 }
156 return NULL;
157}
158
159static void
160pipewire_output_handle_frame(struct pipewire_output *output, int fd,
161 int stride, struct drm_fb *drm_buffer)
162{
163 const struct weston_drm_virtual_output_api *api =
164 output->pipewire->virtual_output_api;
165 size_t size = output->output->height * stride;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200166 struct pw_buffer *buffer;
167 struct spa_buffer *spa_buffer;
168 struct spa_meta_header *h;
169 void *ptr;
170
171 if (pw_stream_get_state(output->stream, NULL) !=
172 PW_STREAM_STATE_STREAMING)
173 goto out;
174
175 buffer = pw_stream_dequeue_buffer(output->stream);
176 if (!buffer) {
177 weston_log("Failed to dequeue a pipewire buffer\n");
178 goto out;
179 }
180
181 spa_buffer = buffer->buffer;
182
James Hilliard80b585f2020-07-06 00:58:02 -0600183 if ((h = spa_buffer_find_meta_data(spa_buffer, SPA_META_Header,
184 sizeof(struct spa_meta_header)))) {
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200185 h->pts = -1;
186 h->flags = 0;
187 h->seq = output->seq++;
188 h->dts_offset = 0;
189 }
190
191 ptr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
192 memcpy(spa_buffer->datas[0].data, ptr, size);
193 munmap(ptr, size);
194
195 spa_buffer->datas[0].chunk->offset = 0;
196 spa_buffer->datas[0].chunk->stride = stride;
197 spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize;
198
199 pipewire_output_debug(output, "push frame");
200 pw_stream_queue_buffer(output->stream, buffer);
201
202out:
203 close(fd);
204 output->submitted_frame = true;
205 api->buffer_released(drm_buffer);
206}
207
208static int
209pipewire_output_fence_sync_handler(int fd, uint32_t mask, void *data)
210{
211 struct pipewire_frame_data *frame_data = data;
212 struct pipewire_output *output = frame_data->output;
213
214 pipewire_output_handle_frame(output, frame_data->fd, frame_data->stride,
215 frame_data->drm_buffer);
216
217 wl_event_source_remove(frame_data->fence_sync_event_source);
218 close(frame_data->fence_sync_fd);
219 free(frame_data);
220
221 return 0;
222}
223
224static int
225pipewire_output_submit_frame(struct weston_output *base_output, int fd,
226 int stride, struct drm_fb *drm_buffer)
227{
228 struct pipewire_output *output = lookup_pipewire_output(base_output);
229 struct weston_pipewire *pipewire = output->pipewire;
230 const struct weston_drm_virtual_output_api *api =
231 pipewire->virtual_output_api;
232 struct wl_event_loop *loop;
233 struct pipewire_frame_data *frame_data;
234 int fence_sync_fd;
235
236 pipewire_output_debug(output, "submit frame: fd = %d drm_fb = %p",
237 fd, drm_buffer);
238
239 fence_sync_fd = api->get_fence_sync_fd(output->output);
240 if (fence_sync_fd == -1) {
241 pipewire_output_handle_frame(output, fd, stride, drm_buffer);
242 return 0;
243 }
244
245 frame_data = zalloc(sizeof *frame_data);
246 if (!frame_data) {
247 close(fence_sync_fd);
248 pipewire_output_handle_frame(output, fd, stride, drm_buffer);
249 return 0;
250 }
251
252 loop = wl_display_get_event_loop(pipewire->compositor->wl_display);
253
254 frame_data->output = output;
255 frame_data->fd = fd;
256 frame_data->stride = stride;
257 frame_data->drm_buffer = drm_buffer;
258 frame_data->fence_sync_fd = fence_sync_fd;
259 frame_data->fence_sync_event_source =
260 wl_event_loop_add_fd(loop, frame_data->fence_sync_fd,
261 WL_EVENT_READABLE,
262 pipewire_output_fence_sync_handler,
263 frame_data);
264
265 return 0;
266}
267
268static void
269pipewire_output_timer_update(struct pipewire_output *output)
270{
271 int64_t msec;
272 int32_t refresh;
273
274 if (pw_stream_get_state(output->stream, NULL) ==
275 PW_STREAM_STATE_STREAMING)
276 refresh = output->output->current_mode->refresh;
277 else
278 refresh = 1000;
279
280 msec = millihz_to_nsec(refresh) / 1000000;
281 wl_event_source_timer_update(output->finish_frame_timer, msec);
282}
283
284static int
285pipewire_output_finish_frame_handler(void *data)
286{
287 struct pipewire_output *output = data;
288 const struct weston_drm_virtual_output_api *api
289 = output->pipewire->virtual_output_api;
290 struct timespec now;
291
292 if (output->submitted_frame) {
293 struct weston_compositor *c = output->pipewire->compositor;
294 output->submitted_frame = false;
295 weston_compositor_read_presentation_clock(c, &now);
296 api->finish_frame(output->output, &now, 0);
297 }
298
Michael Olbricha24a3262020-08-19 08:43:15 +0200299 if (output->dpms == WESTON_DPMS_ON)
300 pipewire_output_timer_update(output);
301 else
302 wl_event_source_timer_update(output->finish_frame_timer, 0);
303
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200304 return 0;
305}
306
307static void
308pipewire_output_destroy(struct weston_output *base_output)
309{
310 struct pipewire_output *output = lookup_pipewire_output(base_output);
311 struct weston_mode *mode, *next;
312
313 wl_list_for_each_safe(mode, next, &base_output->mode_list, link) {
314 wl_list_remove(&mode->link);
315 free(mode);
316 }
317
318 output->saved_destroy(base_output);
319
320 pw_stream_destroy(output->stream);
321
322 wl_list_remove(&output->link);
323 weston_head_release(output->head);
324 free(output->head);
325 free(output);
326}
327
328static int
329pipewire_output_start_repaint_loop(struct weston_output *base_output)
330{
331 struct pipewire_output *output = lookup_pipewire_output(base_output);
332
333 pipewire_output_debug(output, "start repaint loop");
334 output->saved_start_repaint_loop(base_output);
335
336 pipewire_output_timer_update(output);
337
338 return 0;
339}
340
Michael Olbricha24a3262020-08-19 08:43:15 +0200341static void
342pipewire_set_dpms(struct weston_output *base_output, enum dpms_enum level)
343{
344 struct pipewire_output *output = lookup_pipewire_output(base_output);
345
346 if (output->dpms == level)
347 return;
348
349 output->dpms = level;
350 pipewire_output_finish_frame_handler(output);
351}
352
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200353static int
354pipewire_output_connect(struct pipewire_output *output)
355{
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200356 uint8_t buffer[1024];
357 struct spa_pod_builder builder =
358 SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
359 const struct spa_pod *params[1];
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200360 int frame_rate = output->output->current_mode->refresh / 1000;
361 int width = output->output->width;
362 int height = output->output->height;
363 int ret;
364
James Hilliard80b585f2020-07-06 00:58:02 -0600365 params[0] = spa_pod_builder_add_object(&builder,
366 SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
367 SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
368 SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
369 SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_BGRx),
370 SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&SPA_RECTANGLE(width, height)),
371 SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&SPA_FRACTION (0, 1)),
372 SPA_FORMAT_VIDEO_maxFramerate,
373 SPA_POD_CHOICE_RANGE_Fraction(&SPA_FRACTION(frame_rate, 1),
374 &SPA_FRACTION(1, 1),
375 &SPA_FRACTION(frame_rate, 1)));
376
377 ret = pw_stream_connect(output->stream, PW_DIRECTION_OUTPUT, SPA_ID_INVALID,
378 (PW_STREAM_FLAG_DRIVER |
379 PW_STREAM_FLAG_MAP_BUFFERS),
380 params, 1);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200381 if (ret != 0) {
382 weston_log("Failed to connect pipewire stream: %s",
383 spa_strerror(ret));
384 return -1;
385 }
386
387 return 0;
388}
389
390static int
391pipewire_output_enable(struct weston_output *base_output)
392{
393 struct pipewire_output *output = lookup_pipewire_output(base_output);
394 struct weston_compositor *c = base_output->compositor;
395 const struct weston_drm_virtual_output_api *api
396 = output->pipewire->virtual_output_api;
397 struct wl_event_loop *loop;
398 int ret;
399
400 api->set_submit_frame_cb(base_output, pipewire_output_submit_frame);
401
402 ret = pipewire_output_connect(output);
403 if (ret < 0)
404 return ret;
405
406 ret = output->saved_enable(base_output);
407 if (ret < 0)
408 return ret;
409
410 output->saved_start_repaint_loop = base_output->start_repaint_loop;
411 base_output->start_repaint_loop = pipewire_output_start_repaint_loop;
Michael Olbricha24a3262020-08-19 08:43:15 +0200412 base_output->set_dpms = pipewire_set_dpms;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200413
414 loop = wl_display_get_event_loop(c->wl_display);
415 output->finish_frame_timer =
416 wl_event_loop_add_timer(loop,
417 pipewire_output_finish_frame_handler,
418 output);
Michael Olbricha24a3262020-08-19 08:43:15 +0200419 output->dpms = WESTON_DPMS_ON;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200420
421 return 0;
422}
423
424static int
425pipewire_output_disable(struct weston_output *base_output)
426{
427 struct pipewire_output *output = lookup_pipewire_output(base_output);
428
429 wl_event_source_remove(output->finish_frame_timer);
430
431 pw_stream_disconnect(output->stream);
432
433 return output->saved_disable(base_output);
434}
435
436static void
437pipewire_output_stream_state_changed(void *data, enum pw_stream_state old,
438 enum pw_stream_state state,
439 const char *error_message)
440{
441 struct pipewire_output *output = data;
442
443 pipewire_output_debug(output, "state changed %s -> %s",
444 pw_stream_state_as_string(old),
445 pw_stream_state_as_string(state));
446
447 switch (state) {
448 case PW_STREAM_STATE_STREAMING:
449 weston_output_schedule_repaint(output->output);
450 break;
451 default:
452 break;
453 }
454}
455
456static void
James Hilliard80b585f2020-07-06 00:58:02 -0600457pipewire_output_stream_param_changed(void *data, uint32_t id, const struct spa_pod *format)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200458{
459 struct pipewire_output *output = data;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200460 uint8_t buffer[1024];
461 struct spa_pod_builder builder =
462 SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
463 const struct spa_pod *params[2];
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200464 int32_t width, height, stride, size;
465 const int bpp = 4;
466
467 if (!format) {
468 pipewire_output_debug(output, "format = None");
James Hilliard80b585f2020-07-06 00:58:02 -0600469 pw_stream_update_params(output->stream, NULL, 0);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200470 return;
471 }
472
James Hilliard80b585f2020-07-06 00:58:02 -0600473 spa_format_video_raw_parse(format, &output->video_format);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200474
475 width = output->video_format.size.width;
476 height = output->video_format.size.height;
477 stride = SPA_ROUND_UP_N(width * bpp, 4);
478 size = height * stride;
479
480 pipewire_output_debug(output, "format = %dx%d", width, height);
481
James Hilliard80b585f2020-07-06 00:58:02 -0600482 params[0] = spa_pod_builder_add_object(&builder,
483 SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
484 SPA_PARAM_BUFFERS_size, SPA_POD_Int(size),
485 SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride),
486 SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(4, 2, 8),
487 SPA_PARAM_BUFFERS_align, SPA_POD_Int(16));
488
489 params[1] = spa_pod_builder_add_object(&builder,
490 SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
491 SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
492 SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)));
493
494 pw_stream_update_params(output->stream, params, 2);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200495}
496
497static const struct pw_stream_events stream_events = {
498 PW_VERSION_STREAM_EVENTS,
499 .state_changed = pipewire_output_stream_state_changed,
James Hilliard80b585f2020-07-06 00:58:02 -0600500 .param_changed = pipewire_output_stream_param_changed,
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200501};
502
503static struct weston_output *
504pipewire_output_create(struct weston_compositor *c, char *name)
505{
506 struct weston_pipewire *pipewire = weston_pipewire_get(c);
507 struct pipewire_output *output;
508 struct weston_head *head;
509 const struct weston_drm_virtual_output_api *api;
510 const char *make = "Weston";
511 const char *model = "Virtual Display";
512 const char *serial_number = "unknown";
513 const char *connector_name = "pipewire";
Marius Vladbb7ed372020-11-05 19:30:51 +0200514 char *remoting_name;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200515
516 if (!name || !strlen(name))
517 return NULL;
518
519 api = pipewire->virtual_output_api;
520
521 output = zalloc(sizeof *output);
522 if (!output)
523 return NULL;
524
525 head = zalloc(sizeof *head);
526 if (!head)
527 goto err;
528
James Hilliard80b585f2020-07-06 00:58:02 -0600529 output->stream = pw_stream_new(pipewire->core, name, NULL);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200530 if (!output->stream) {
531 weston_log("Cannot initialize pipewire stream\n");
532 goto err;
533 }
534
535 pw_stream_add_listener(output->stream, &output->stream_listener,
536 &stream_events, output);
537
538 output->output = api->create_output(c, name);
539 if (!output->output) {
540 weston_log("Cannot create virtual output\n");
541 goto err;
542 }
543
544 output->saved_destroy = output->output->destroy;
545 output->output->destroy = pipewire_output_destroy;
546 output->saved_enable = output->output->enable;
547 output->output->enable = pipewire_output_enable;
548 output->saved_disable = output->output->disable;
549 output->output->disable = pipewire_output_disable;
550 output->pipewire = pipewire;
551 wl_list_insert(pipewire->output_list.prev, &output->link);
552
Marius Vladbb7ed372020-11-05 19:30:51 +0200553 asprintf(&remoting_name, "%s-%s", connector_name, name);
554 weston_head_init(head, remoting_name);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200555 weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_NONE);
556 weston_head_set_monitor_strings(head, make, model, serial_number);
557 head->compositor = c;
558 output->head = head;
559
560 weston_output_attach_head(output->output, head);
561
Marius Vladbb7ed372020-11-05 19:30:51 +0200562 free(remoting_name);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200563 pipewire_output_debug(output, "created");
564
565 return output->output;
566err:
567 if (output->stream)
568 pw_stream_destroy(output->stream);
569 if (head)
570 free(head);
571 free(output);
572 return NULL;
573}
574
575static bool
576pipewire_output_is_pipewire(struct weston_output *output)
577{
578 return lookup_pipewire_output(output) != NULL;
579}
580
581static int
582pipewire_output_set_mode(struct weston_output *base_output, const char *modeline)
583{
584 struct pipewire_output *output = lookup_pipewire_output(base_output);
585 const struct weston_drm_virtual_output_api *api =
586 output->pipewire->virtual_output_api;
587 struct weston_mode *mode;
588 int n, width, height, refresh = 0;
589
590 if (output == NULL) {
591 weston_log("Output is not pipewire.\n");
592 return -1;
593 }
594
595 if (!modeline)
596 return -1;
597
598 n = sscanf(modeline, "%dx%d@%d", &width, &height, &refresh);
599 if (n != 2 && n != 3)
600 return -1;
601
602 if (pw_stream_get_state(output->stream, NULL) !=
603 PW_STREAM_STATE_UNCONNECTED) {
604 return -1;
605 }
606
607 mode = zalloc(sizeof *mode);
608 if (!mode)
609 return -1;
610
611 pipewire_output_debug(output, "mode = %dx%d@%d", width, height, refresh);
612
613 mode->flags = WL_OUTPUT_MODE_CURRENT;
614 mode->width = width;
615 mode->height = height;
616 mode->refresh = (refresh ? refresh : 60) * 1000LL;
617
618 wl_list_insert(base_output->mode_list.prev, &mode->link);
619
620 base_output->current_mode = mode;
621
622 api->set_gbm_format(base_output, "XRGB8888");
623
624 return 0;
625}
626
627static void
628pipewire_output_set_seat(struct weston_output *output, const char *seat)
629{
630}
631
632static void
633weston_pipewire_destroy(struct wl_listener *l, void *data)
634{
635 struct weston_pipewire *pipewire =
636 wl_container_of(l, pipewire, destroy_listener);
637
Leandro Ribeirof0149642019-12-18 15:52:18 -0300638 weston_log_scope_destroy(pipewire->debug);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200639 pipewire->debug = NULL;
640
641 wl_event_source_remove(pipewire->loop_source);
642 pw_loop_leave(pipewire->loop);
643 pw_loop_destroy(pipewire->loop);
644}
645
646static struct weston_pipewire *
647weston_pipewire_get(struct weston_compositor *compositor)
648{
649 struct wl_listener *listener;
650 struct weston_pipewire *pipewire;
651
652 listener = wl_signal_get(&compositor->destroy_signal,
653 weston_pipewire_destroy);
654 if (!listener)
655 return NULL;
656
657 pipewire = wl_container_of(listener, pipewire, destroy_listener);
658 return pipewire;
659}
660
661static int
662weston_pipewire_loop_handler(int fd, uint32_t mask, void *data)
663{
664 struct weston_pipewire *pipewire = data;
665 int ret;
666
667 ret = pw_loop_iterate(pipewire->loop, 0);
668 if (ret < 0)
669 weston_log("pipewire_loop_iterate failed: %s",
670 spa_strerror(ret));
671
672 return 0;
673}
674
James Hilliard80b585f2020-07-06 00:58:02 -0600675static void
676weston_pipewire_error(void *data, uint32_t id, int seq, int res,
677 const char *error)
678{
679 weston_log("pipewire remote error: %s\n", error);
680}
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200681
James Hilliard80b585f2020-07-06 00:58:02 -0600682static const struct pw_core_events core_events = {
683 PW_VERSION_CORE_EVENTS,
684 .error = weston_pipewire_error,
685};
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200686
687static int
688weston_pipewire_init(struct weston_pipewire *pipewire)
689{
690 struct wl_event_loop *loop;
691
692 pw_init(NULL, NULL);
693
694 pipewire->loop = pw_loop_new(NULL);
695 if (!pipewire->loop)
696 return -1;
697
698 pw_loop_enter(pipewire->loop);
699
James Hilliard80b585f2020-07-06 00:58:02 -0600700 pipewire->context = pw_context_new(pipewire->loop, NULL, 0);
Ambareesh Balajia67393c2020-12-05 20:49:55 -0500701 pipewire->core = pw_context_connect(pipewire->context, NULL, 0);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200702
James Hilliard80b585f2020-07-06 00:58:02 -0600703 pw_core_add_listener(pipewire->core,
704 &pipewire->core_listener,
705 &core_events, pipewire);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200706
707 loop = wl_display_get_event_loop(pipewire->compositor->wl_display);
708 pipewire->loop_source =
709 wl_event_loop_add_fd(loop, pw_loop_get_fd(pipewire->loop),
710 WL_EVENT_READABLE,
711 weston_pipewire_loop_handler,
712 pipewire);
713
714 return 0;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200715}
716
717static const struct weston_pipewire_api pipewire_api = {
718 pipewire_output_create,
719 pipewire_output_is_pipewire,
720 pipewire_output_set_mode,
721 pipewire_output_set_seat,
722};
723
724WL_EXPORT int
725weston_module_init(struct weston_compositor *compositor)
726{
727 int ret;
728 struct weston_pipewire *pipewire;
729 const struct weston_drm_virtual_output_api *api =
730 weston_drm_virtual_output_get_api(compositor);
731
732 if (!api)
733 return -1;
734
735 pipewire = zalloc(sizeof *pipewire);
736 if (!pipewire)
737 return -1;
738
Pekka Paalanen6ffbba32019-11-06 12:59:32 +0200739 if (!weston_compositor_add_destroy_listener_once(compositor,
740 &pipewire->destroy_listener,
741 weston_pipewire_destroy)) {
742 free(pipewire);
743 return 0;
744 }
745
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200746 pipewire->virtual_output_api = api;
747 pipewire->compositor = compositor;
748 wl_list_init(&pipewire->output_list);
749
750 ret = weston_plugin_api_register(compositor, WESTON_PIPEWIRE_API_NAME,
751 &pipewire_api, sizeof(pipewire_api));
752
753 if (ret < 0) {
754 weston_log("Failed to register pipewire API.\n");
755 goto failed;
756 }
757
758 ret = weston_pipewire_init(pipewire);
759 if (ret < 0) {
760 weston_log("Failed to initialize pipewire.\n");
761 goto failed;
762 }
763
Leandro Ribeiro5976dbb2019-12-18 15:21:03 -0300764 pipewire->debug =
Leandro Ribeiro23289352019-12-26 16:44:56 -0300765 weston_compositor_add_log_scope(compositor, "pipewire",
766 "Debug messages from pipewire plugin\n",
767 NULL, NULL, NULL);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200768
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200769 return 0;
770
771failed:
Pekka Paalanen6ffbba32019-11-06 12:59:32 +0200772 wl_list_remove(&pipewire->destroy_listener.link);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200773 free(pipewire);
774 return -1;
775}