1 /*
2 * Copyright © 2014, 2015 Collabora, Ltd.
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
26 #include "config.h"
27
28 #include <assert.h>
29 #include <stdint.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32
33 #include <libweston/libweston.h>
34 #include "linux-dmabuf.h"
35 #include "linux-dmabuf-unstable-v1-server-protocol.h"
36 #include "libweston-internal.h"
37
38 static void
linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer * buffer)39 linux_dmabuf_buffer_destroy(struct linux_dmabuf_buffer *buffer)
40 {
41 int i;
42
43 for (i = 0; i < buffer->attributes.n_planes; i++) {
44 close(buffer->attributes.fd[i]);
45 buffer->attributes.fd[i] = -1;
46 }
47
48 buffer->attributes.n_planes = 0;
49
50 if (buffer->attributes.buffer_handle != NULL) {
51 free(buffer->attributes.buffer_handle);
52 }
53 free(buffer);
54 }
55
56 static void
destroy_params(struct wl_resource * params_resource)57 destroy_params(struct wl_resource *params_resource)
58 {
59 struct linux_dmabuf_buffer *buffer;
60
61 buffer = wl_resource_get_user_data(params_resource);
62
63 if (!buffer)
64 return;
65
66 linux_dmabuf_buffer_destroy(buffer);
67 }
68
69 static void
params_destroy(struct wl_client * client,struct wl_resource * resource)70 params_destroy(struct wl_client *client, struct wl_resource *resource)
71 {
72 wl_resource_destroy(resource);
73 }
74
75 static void
params_add(struct wl_client * client,struct wl_resource * params_resource,int32_t name_fd,uint32_t plane_idx,uint32_t offset,uint32_t stride,uint32_t modifier_hi,uint32_t modifier_lo)76 params_add(struct wl_client *client,
77 struct wl_resource *params_resource,
78 int32_t name_fd,
79 uint32_t plane_idx,
80 uint32_t offset,
81 uint32_t stride,
82 uint32_t modifier_hi,
83 uint32_t modifier_lo)
84 {
85 struct linux_dmabuf_buffer *buffer;
86
87 buffer = wl_resource_get_user_data(params_resource);
88 if (!buffer) {
89 wl_resource_post_error(params_resource,
90 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
91 "params was already used to create a wl_buffer");
92 close(name_fd);
93 return;
94 }
95
96 assert(buffer->params_resource == params_resource);
97 assert(!buffer->buffer_resource);
98
99 if (plane_idx >= MAX_DMABUF_PLANES) {
100 wl_resource_post_error(params_resource,
101 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
102 "plane index %u is too high", plane_idx);
103 close(name_fd);
104 return;
105 }
106
107 if (buffer->attributes.fd[plane_idx] != -1) {
108 wl_resource_post_error(params_resource,
109 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
110 "a dmabuf has already been added for plane %u",
111 plane_idx);
112 close(name_fd);
113 return;
114 }
115
116 buffer->attributes.fd[plane_idx] = name_fd;
117 buffer->attributes.offset[plane_idx] = offset;
118 buffer->attributes.stride[plane_idx] = stride;
119
120 if (wl_resource_get_version(params_resource) < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION)
121 buffer->attributes.modifier[plane_idx] = DRM_FORMAT_MOD_INVALID;
122 else
123 buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) |
124 modifier_lo;
125
126 buffer->attributes.n_planes++;
127 }
128
129 static void
params_add_buffer_handle(struct wl_client * client,struct wl_resource * params_resource,int32_t fd,int32_t width,int32_t stride,int32_t height,int32_t size,int32_t format,uint32_t usage_hi,uint32_t usage_lo,uint32_t phyaddr_hi,uint32_t phyaddr_lo,int32_t key,struct wl_array * reservefds,struct wl_array * reserveints)130 params_add_buffer_handle(struct wl_client *client, struct wl_resource *params_resource,
131 int32_t fd, int32_t width, int32_t stride, int32_t height, int32_t size,
132 int32_t format, uint32_t usage_hi, uint32_t usage_lo, uint32_t phyaddr_hi,
133 uint32_t phyaddr_lo, int32_t key, struct wl_array *reservefds, struct wl_array *reserveints)
134 {
135 struct linux_dmabuf_buffer *buffer;
136 buffer = wl_resource_get_user_data(params_resource);
137 if (!buffer) {
138 wl_resource_post_error(params_resource,
139 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
140 "params was already used to create a wl_buffer");
141 return;
142 }
143
144 uint32_t reserveFds = reservefds->size / sizeof(uint32_t);
145 uint32_t reserveInts = reserveints->size / sizeof(uint32_t);
146
147 buffer->attributes.buffer_handle = zalloc((sizeof(BufferHandle)) + (reserveFds + reserveInts) * sizeof(int32_t));
148 if (!buffer->attributes.buffer_handle) {
149 wl_resource_post_no_memory(params_resource);
150 return;
151 }
152
153 buffer->attributes.buffer_handle->fd = buffer->attributes.fd[0];
154 buffer->attributes.buffer_handle->width = width;
155 buffer->attributes.buffer_handle->stride = stride;
156 buffer->attributes.buffer_handle->height = height;
157 buffer->attributes.buffer_handle->size = size;
158 buffer->attributes.buffer_handle->format = format;
159 buffer->attributes.buffer_handle->usage = ((uint64_t)(usage_hi) << 32) | usage_lo;
160 buffer->attributes.buffer_handle->phyAddr = ((uint64_t)(phyaddr_hi) << 32) | phyaddr_lo;;
161 buffer->attributes.buffer_handle->key = key;
162 buffer->attributes.buffer_handle->reserveFds = reserveFds;
163 buffer->attributes.buffer_handle->reserveInts = reserveInts;
164
165 uint32_t *p;
166 int i = 0;
167 wl_array_for_each(p, reservefds) {
168 buffer->attributes.buffer_handle->reserve[i] = *p;
169 i++;
170 }
171 wl_array_for_each(p, reserveints) {
172 buffer->attributes.buffer_handle->reserve[i] = *p;
173 i++;
174 }
175 }
176
177 static void
linux_dmabuf_wl_buffer_destroy(struct wl_client * client,struct wl_resource * resource)178 linux_dmabuf_wl_buffer_destroy(struct wl_client *client,
179 struct wl_resource *resource)
180 {
181 wl_resource_destroy(resource);
182 }
183
184 static const struct wl_buffer_interface linux_dmabuf_buffer_implementation = {
185 linux_dmabuf_wl_buffer_destroy
186 };
187
188 static void
destroy_linux_dmabuf_wl_buffer(struct wl_resource * resource)189 destroy_linux_dmabuf_wl_buffer(struct wl_resource *resource)
190 {
191 struct linux_dmabuf_buffer *buffer;
192
193 buffer = wl_resource_get_user_data(resource);
194 assert(buffer->buffer_resource == resource);
195 assert(!buffer->params_resource);
196
197 if (buffer->user_data_destroy_func)
198 buffer->user_data_destroy_func(buffer);
199
200 linux_dmabuf_buffer_destroy(buffer);
201 }
202
203 static void
params_create_common(struct wl_client * client,struct wl_resource * params_resource,uint32_t buffer_id,int32_t width,int32_t height,uint32_t format,uint32_t flags)204 params_create_common(struct wl_client *client,
205 struct wl_resource *params_resource,
206 uint32_t buffer_id,
207 int32_t width,
208 int32_t height,
209 uint32_t format,
210 uint32_t flags)
211 {
212 struct linux_dmabuf_buffer *buffer;
213 int i;
214
215 buffer = wl_resource_get_user_data(params_resource);
216
217 if (!buffer) {
218 wl_resource_post_error(params_resource,
219 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
220 "params was already used to create a wl_buffer");
221 return;
222 }
223
224 assert(buffer->params_resource == params_resource);
225 assert(!buffer->buffer_resource);
226
227 /* Switch the linux_dmabuf_buffer object from params resource to
228 * eventually wl_buffer resource.
229 */
230 wl_resource_set_user_data(buffer->params_resource, NULL);
231 buffer->params_resource = NULL;
232
233 if (!buffer->attributes.n_planes) {
234 wl_resource_post_error(params_resource,
235 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
236 "no dmabuf has been added to the params");
237 goto err_out;
238 }
239
240 /* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */
241 for (i = 0; i < buffer->attributes.n_planes; i++) {
242 if (buffer->attributes.fd[i] == -1) {
243 wl_resource_post_error(params_resource,
244 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
245 "no dmabuf has been added for plane %i", i);
246 goto err_out;
247 }
248 }
249
250 buffer->attributes.width = width;
251 buffer->attributes.height = height;
252 buffer->attributes.format = format;
253 buffer->attributes.flags = flags;
254
255 if (width < 1 || height < 1) {
256 wl_resource_post_error(params_resource,
257 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
258 "invalid width %d or height %d", width, height);
259 goto err_out;
260 }
261
262 for (i = 0; i < buffer->attributes.n_planes; i++) {
263 off_t size;
264
265 if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) {
266 wl_resource_post_error(params_resource,
267 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
268 "size overflow for plane %i", i);
269 goto err_out;
270 }
271
272 if (i == 0 &&
273 (uint64_t) buffer->attributes.offset[i] +
274 (uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) {
275 wl_resource_post_error(params_resource,
276 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
277 "size overflow for plane %i", i);
278 goto err_out;
279 }
280
281 /* Don't report an error as it might be caused
282 * by the kernel not supporting seeking on dmabuf */
283 size = lseek(buffer->attributes.fd[i], 0, SEEK_END);
284 if (size == -1)
285 continue;
286
287 if (buffer->attributes.offset[i] >= size) {
288 wl_resource_post_error(params_resource,
289 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
290 "invalid offset %i for plane %i",
291 buffer->attributes.offset[i], i);
292 goto err_out;
293 }
294
295 if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) {
296 wl_resource_post_error(params_resource,
297 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
298 "invalid stride %i for plane %i",
299 buffer->attributes.stride[i], i);
300 goto err_out;
301 }
302
303 /* Only valid for first plane as other planes might be
304 * sub-sampled according to fourcc format */
305 if (i == 0 &&
306 buffer->attributes.offset[i] + buffer->attributes.stride[i] * height > size) {
307 wl_resource_post_error(params_resource,
308 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
309 "invalid buffer stride or height for plane %i", i);
310 goto err_out;
311 }
312 }
313
314 if (buffer->direct_display) {
315 if (!weston_compositor_dmabuf_can_scanout(buffer->compositor,
316 buffer))
317 goto err_failed;
318
319 goto avoid_gpu_import;
320 }
321
322 if (!weston_compositor_import_dmabuf(buffer->compositor, buffer))
323 goto err_failed;
324
325 avoid_gpu_import:
326 buffer->buffer_resource = wl_resource_create(client,
327 &wl_buffer_interface,
328 1, buffer_id);
329 if (!buffer->buffer_resource) {
330 wl_resource_post_no_memory(params_resource);
331 goto err_buffer;
332 }
333
334 wl_resource_set_implementation(buffer->buffer_resource,
335 &linux_dmabuf_buffer_implementation,
336 buffer, destroy_linux_dmabuf_wl_buffer);
337
338 /* send 'created' event when the request is not for an immediate
339 * import, ie buffer_id is zero */
340 if (buffer_id == 0)
341 zwp_linux_buffer_params_v1_send_created(params_resource,
342 buffer->buffer_resource);
343
344 return;
345
346 err_buffer:
347 if (buffer->user_data_destroy_func)
348 buffer->user_data_destroy_func(buffer);
349
350 err_failed:
351 if (buffer_id == 0)
352 zwp_linux_buffer_params_v1_send_failed(params_resource);
353 else
354 /* since the behavior is left implementation defined by the
355 * protocol in case of create_immed failure due to an unknown cause,
356 * we choose to treat it as a fatal error and immediately kill the
357 * client instead of creating an invalid handle and waiting for it
358 * to be used.
359 */
360 wl_resource_post_error(params_resource,
361 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
362 "importing the supplied dmabufs failed");
363
364 err_out:
365 linux_dmabuf_buffer_destroy(buffer);
366 }
367
368 static void
params_create(struct wl_client * client,struct wl_resource * params_resource,int32_t width,int32_t height,uint32_t format,uint32_t flags)369 params_create(struct wl_client *client,
370 struct wl_resource *params_resource,
371 int32_t width,
372 int32_t height,
373 uint32_t format,
374 uint32_t flags)
375 {
376 params_create_common(client, params_resource, 0, width, height, format,
377 flags);
378 }
379
380 static void
params_create_immed(struct wl_client * client,struct wl_resource * params_resource,uint32_t buffer_id,int32_t width,int32_t height,uint32_t format,uint32_t flags)381 params_create_immed(struct wl_client *client,
382 struct wl_resource *params_resource,
383 uint32_t buffer_id,
384 int32_t width,
385 int32_t height,
386 uint32_t format,
387 uint32_t flags)
388 {
389 params_create_common(client, params_resource, buffer_id, width, height,
390 format, flags);
391 }
392
393 static const struct zwp_linux_buffer_params_v1_interface
394 zwp_linux_buffer_params_implementation = {
395 params_destroy,
396 params_add,
397 params_add_buffer_handle,
398 params_create,
399 params_create_immed
400 };
401
402 static void
linux_dmabuf_destroy(struct wl_client * client,struct wl_resource * resource)403 linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource)
404 {
405 wl_resource_destroy(resource);
406 }
407
408 static void
linux_dmabuf_create_params(struct wl_client * client,struct wl_resource * linux_dmabuf_resource,uint32_t params_id)409 linux_dmabuf_create_params(struct wl_client *client,
410 struct wl_resource *linux_dmabuf_resource,
411 uint32_t params_id)
412 {
413 struct weston_compositor *compositor;
414 struct linux_dmabuf_buffer *buffer;
415 uint32_t version;
416 int i;
417
418 version = wl_resource_get_version(linux_dmabuf_resource);
419 compositor = wl_resource_get_user_data(linux_dmabuf_resource);
420
421 buffer = zalloc(sizeof *buffer);
422 if (!buffer)
423 goto err_out;
424
425 for (i = 0; i < MAX_DMABUF_PLANES; i++)
426 buffer->attributes.fd[i] = -1;
427
428 buffer->compositor = compositor;
429 buffer->params_resource =
430 wl_resource_create(client,
431 &zwp_linux_buffer_params_v1_interface,
432 version, params_id);
433 buffer->direct_display = false;
434 if (!buffer->params_resource)
435 goto err_dealloc;
436
437 wl_resource_set_implementation(buffer->params_resource,
438 &zwp_linux_buffer_params_implementation,
439 buffer, destroy_params);
440
441 return;
442
443 err_dealloc:
444 free(buffer);
445
446 err_out:
447 wl_resource_post_no_memory(linux_dmabuf_resource);
448 }
449
450 /** Get the linux_dmabuf_buffer from a wl_buffer resource
451 *
452 * If the given wl_buffer resource was created through the linux_dmabuf
453 * protocol interface, returns the linux_dmabuf_buffer object. This can
454 * be used as a type check for a wl_buffer.
455 *
456 * \param resource A wl_buffer resource.
457 * \return The linux_dmabuf_buffer if it exists, or NULL otherwise.
458 */
459 WL_EXPORT struct linux_dmabuf_buffer *
linux_dmabuf_buffer_get(struct wl_resource * resource)460 linux_dmabuf_buffer_get(struct wl_resource *resource)
461 {
462 struct linux_dmabuf_buffer *buffer;
463
464 if (!resource)
465 return NULL;
466
467 if (!wl_resource_instance_of(resource, &wl_buffer_interface,
468 &linux_dmabuf_buffer_implementation))
469 return NULL;
470
471 buffer = wl_resource_get_user_data(resource);
472 assert(buffer);
473 assert(!buffer->params_resource);
474 assert(buffer->buffer_resource == resource);
475
476 return buffer;
477 }
478
479 /** Set renderer-private data
480 *
481 * Set the user data for the linux_dmabuf_buffer. It is invalid to overwrite
482 * a non-NULL user data with a new non-NULL pointer. This is meant to
483 * protect against renderers fighting over linux_dmabuf_buffer user data
484 * ownership.
485 *
486 * The renderer-private data is usually set from the
487 * weston_renderer::import_dmabuf hook.
488 *
489 * \param buffer The linux_dmabuf_buffer object to set for.
490 * \param data The new renderer-private data pointer.
491 * \param func Destructor function to be called for the renderer-private
492 * data when the linux_dmabuf_buffer gets destroyed.
493 *
494 * \sa weston_compositor_import_dmabuf
495 */
496 WL_EXPORT void
linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer * buffer,void * data,dmabuf_user_data_destroy_func func)497 linux_dmabuf_buffer_set_user_data(struct linux_dmabuf_buffer *buffer,
498 void *data,
499 dmabuf_user_data_destroy_func func)
500 {
501 assert(data == NULL || buffer->user_data == NULL);
502
503 buffer->user_data = data;
504 buffer->user_data_destroy_func = func;
505 }
506
507 /** Get renderer-private data
508 *
509 * Get the user data from the linux_dmabuf_buffer.
510 *
511 * \param buffer The linux_dmabuf_buffer to query.
512 * \return Renderer-private data pointer.
513 *
514 * \sa linux_dmabuf_buffer_set_user_data
515 */
516 WL_EXPORT void *
linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer * buffer)517 linux_dmabuf_buffer_get_user_data(struct linux_dmabuf_buffer *buffer)
518 {
519 return buffer->user_data;
520 }
521
522 static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_implementation = {
523 linux_dmabuf_destroy,
524 linux_dmabuf_create_params
525 };
526
527 static void
bind_linux_dmabuf(struct wl_client * client,void * data,uint32_t version,uint32_t id)528 bind_linux_dmabuf(struct wl_client *client,
529 void *data, uint32_t version, uint32_t id)
530 {
531 struct weston_compositor *compositor = data;
532 struct wl_resource *resource;
533 int *formats = NULL;
534 uint64_t *modifiers = NULL;
535 int num_formats, num_modifiers;
536 uint64_t modifier_invalid = DRM_FORMAT_MOD_INVALID;
537 int i, j;
538
539 resource = wl_resource_create(client, &zwp_linux_dmabuf_v1_interface,
540 version, id);
541 if (resource == NULL) {
542 wl_client_post_no_memory(client);
543 return;
544 }
545
546 wl_resource_set_implementation(resource, &linux_dmabuf_implementation,
547 compositor, NULL);
548
549 /*
550 * Use EGL_EXT_image_dma_buf_import_modifiers to query and advertise
551 * format/modifier codes.
552 */
553 compositor->renderer->query_dmabuf_formats(compositor, &formats,
554 &num_formats);
555
556 for (i = 0; i < num_formats; i++) {
557 compositor->renderer->query_dmabuf_modifiers(compositor,
558 formats[i],
559 &modifiers,
560 &num_modifiers);
561
562 /* send DRM_FORMAT_MOD_INVALID token when no modifiers are supported
563 * for this format */
564 if (num_modifiers == 0) {
565 num_modifiers = 1;
566 modifiers = &modifier_invalid;
567 }
568 for (j = 0; j < num_modifiers; j++) {
569 if (version >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
570 uint32_t modifier_lo = modifiers[j] & 0xFFFFFFFF;
571 uint32_t modifier_hi = modifiers[j] >> 32;
572 zwp_linux_dmabuf_v1_send_modifier(resource,
573 formats[i],
574 modifier_hi,
575 modifier_lo);
576 } else if (modifiers[j] == DRM_FORMAT_MOD_LINEAR ||
577 modifiers == &modifier_invalid) {
578 zwp_linux_dmabuf_v1_send_format(resource,
579 formats[i]);
580 }
581 }
582 if (modifiers != &modifier_invalid)
583 free(modifiers);
584 }
585 free(formats);
586 }
587
588 /** Advertise linux_dmabuf support
589 *
590 * Calling this initializes the zwp_linux_dmabuf protocol support, so that
591 * the interface will be advertised to clients. Essentially it creates a
592 * global. Do not call this function multiple times in the compositor's
593 * lifetime. There is no way to deinit explicitly, globals will be reaped
594 * when the wl_display gets destroyed.
595 *
596 * \param compositor The compositor to init for.
597 * \return Zero on success, -1 on failure.
598 */
599 WL_EXPORT int
linux_dmabuf_setup(struct weston_compositor * compositor)600 linux_dmabuf_setup(struct weston_compositor *compositor)
601 {
602 if (!wl_global_create(compositor->wl_display,
603 &zwp_linux_dmabuf_v1_interface, 3,
604 compositor, bind_linux_dmabuf))
605 return -1;
606
607 return 0;
608 }
609
610 /** Resolve an internal compositor error by disconnecting the client.
611 *
612 * This function is used in cases when the dmabuf-based wl_buffer
613 * turns out unusable and there is no fallback path. This is used by
614 * renderers which are the fallback path in the first place.
615 *
616 * It is possible the fault is caused by a compositor bug, the underlying
617 * graphics stack bug or normal behaviour, or perhaps a client mistake.
618 * In any case, the options are to either composite garbage or nothing,
619 * or disconnect the client. This is a helper function for the latter.
620 *
621 * The error is sent as an INVALID_OBJECT error on the client's wl_display.
622 *
623 * \param buffer The linux_dmabuf_buffer that is unusable.
624 * \param msg A custom error message attached to the protocol error.
625 */
626 WL_EXPORT void
linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer * buffer,const char * msg)627 linux_dmabuf_buffer_send_server_error(struct linux_dmabuf_buffer *buffer,
628 const char *msg)
629 {
630 struct wl_client *client;
631 struct wl_resource *display_resource;
632 uint32_t id;
633
634 assert(buffer->buffer_resource);
635 id = wl_resource_get_id(buffer->buffer_resource);
636 client = wl_resource_get_client(buffer->buffer_resource);
637 display_resource = wl_client_get_object(client, 1);
638
639 assert(display_resource);
640 wl_resource_post_error(display_resource,
641 WL_DISPLAY_ERROR_INVALID_OBJECT,
642 "linux_dmabuf server error with "
643 "wl_buffer@%u: %s", id, msg);
644 }
645