diff --git a/include/buffer.h b/include/buffer.h index fd24a20..bde224e 100644 --- a/include/buffer.h +++ b/include/buffer.h @@ -22,18 +22,33 @@ #include struct wl_buffer; +struct gbm_bo; + +enum buffer_type { + BUFFER_UNSPEC = 0, + BUFFER_WL_SHM, + BUFFER_DMABUF, +}; struct buffer { - int width, height, stride; + enum buffer_type type; + + int width, height; size_t size; uint32_t format; struct wl_buffer* wl_buffer; - void* pixels; bool is_attached; bool please_clean_up; struct pixman_region16 damage; + + // wl_shm: + void* pixels; + int stride; + + // dmabuf: + struct gbm_bo* bo; }; -struct buffer* buffer_create(int width, int height, int stride, uint32_t format); +struct buffer* buffer_create_shm(int width, int height, int stride, uint32_t format); +struct buffer* buffer_create_dmabuf(int width, int height, uint32_t format); void buffer_destroy(struct buffer* self); - diff --git a/meson.build b/meson.build index 64025d2..0b1e80c 100644 --- a/meson.build +++ b/meson.build @@ -33,6 +33,8 @@ xkbcommon = dependency('xkbcommon') pixman = dependency('pixman-1') wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') +drm = dependency('libdrm') +gbm = dependency('gbm') libvncserver_opt = cmake.subproject_options() libvncserver_opt.add_cmake_defines({ @@ -83,6 +85,8 @@ dependencies = [ aml, wayland_client, wayland_cursor, + drm, + gbm, libvncclient, client_protos, ] diff --git a/protocols/linux-dmabuf-unstable-v1.xml b/protocols/linux-dmabuf-unstable-v1.xml new file mode 100644 index 0000000..018f876 --- /dev/null +++ b/protocols/linux-dmabuf-unstable-v1.xml @@ -0,0 +1,586 @@ + + + + + Copyright © 2014, 2015 Collabora, Ltd. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + Following the interfaces from: + https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_image_dma_buf_import.txt + https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_image_dma_buf_import_modifiers.txt + and the Linux DRM sub-system's AddFb2 ioctl. + + This interface offers ways to create generic dmabuf-based wl_buffers. + + Clients can use the get_surface_feedback request to get dmabuf feedback + for a particular surface. If the client wants to retrieve feedback not + tied to a surface, they can use the get_default_feedback request. + + The following are required from clients: + + - Clients must ensure that either all data in the dma-buf is + coherent for all subsequent read access or that coherency is + correctly handled by the underlying kernel-side dma-buf + implementation. + + - Don't make any more attachments after sending the buffer to the + compositor. Making more attachments later increases the risk of + the compositor not being able to use (re-import) an existing + dmabuf-based wl_buffer. + + The underlying graphics stack must ensure the following: + + - The dmabuf file descriptors relayed to the server will stay valid + for the whole lifetime of the wl_buffer. This means the server may + at any time use those fds to import the dmabuf into any kernel + sub-system that might accept it. + + However, when the underlying graphics stack fails to deliver the + promise, because of e.g. a device hot-unplug which raises internal + errors, after the wl_buffer has been successfully created the + compositor must not raise protocol errors to the client when dmabuf + import later fails. + + To create a wl_buffer from one or more dmabufs, a client creates a + zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params + request. All planes required by the intended format are added with + the 'add' request. Finally, a 'create' or 'create_immed' request is + issued, which has the following outcome depending on the import success. + + The 'create' request, + - on success, triggers a 'created' event which provides the final + wl_buffer to the client. + - on failure, triggers a 'failed' event to convey that the server + cannot use the dmabufs received from the client. + + For the 'create_immed' request, + - on success, the server immediately imports the added dmabufs to + create a wl_buffer. No event is sent from the server in this case. + - on failure, the server can choose to either: + - terminate the client by raising a fatal error. + - mark the wl_buffer as failed, and send a 'failed' event to the + client. If the client uses a failed wl_buffer as an argument to any + request, the behaviour is compositor implementation-defined. + + For all DRM formats and unless specified in another protocol extension, + pre-multiplied alpha is used for pixel values. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + + + + + Objects created through this interface, especially wl_buffers, will + remain valid. + + + + + + This temporary object is used to collect multiple dmabuf handles into + a single batch to create a wl_buffer. It can only be used once and + should be destroyed after a 'created' or 'failed' event has been + received. + + + + + + + This event advertises one buffer format that the server supports. + All the supported formats are advertised once when the client + binds to this interface. A roundtrip after binding guarantees + that the client has received all supported formats. + + For the definition of the format codes, see the + zwp_linux_buffer_params_v1::create request. + + Starting version 4, the format event is deprecated and must not be + sent by compositors. Instead, use get_default_feedback or + get_surface_feedback. + + + + + + + This event advertises the formats that the server supports, along with + the modifiers supported for each format. All the supported modifiers + for all the supported formats are advertised once when the client + binds to this interface. A roundtrip after binding guarantees that + the client has received all supported format-modifier pairs. + + For legacy support, DRM_FORMAT_MOD_INVALID (that is, modifier_hi == + 0x00ffffff and modifier_lo == 0xffffffff) is allowed in this event. + It indicates that the server can support the format with an implicit + modifier. When a plane has DRM_FORMAT_MOD_INVALID as its modifier, it + is as if no explicit modifier is specified. The effective modifier + will be derived from the dmabuf. + + A compositor that sends valid modifiers and DRM_FORMAT_MOD_INVALID for + a given format supports both explicit modifiers and implicit modifiers. + + For the definition of the format and modifier codes, see the + zwp_linux_buffer_params_v1::create and zwp_linux_buffer_params_v1::add + requests. + + Starting version 4, the modifier event is deprecated and must not be + sent by compositors. Instead, use get_default_feedback or + get_surface_feedback. + + + + + + + + + + + This request creates a new wp_linux_dmabuf_feedback object not bound + to a particular surface. This object will deliver feedback about dmabuf + parameters to use if the client doesn't support per-surface feedback + (see get_surface_feedback). + + + + + + + This request creates a new wp_linux_dmabuf_feedback object for the + specified wl_surface. This object will deliver feedback about dmabuf + parameters to use for buffers attached to this surface. + + If the surface is destroyed before the wp_linux_dmabuf_feedback object, + the feedback object becomes inert. + + + + + + + + + This temporary object is a collection of dmabufs and other + parameters that together form a single logical buffer. The temporary + object may eventually create one wl_buffer unless cancelled by + destroying it before requesting 'create'. + + Single-planar formats only require one dmabuf, however + multi-planar formats may require more than one dmabuf. For all + formats, an 'add' request must be called once per plane (even if the + underlying dmabuf fd is identical). + + You must use consecutive plane indices ('plane_idx' argument for 'add') + from zero to the number of planes used by the drm_fourcc format code. + All planes required by the format must be given exactly once, but can + be given in any order. Each plane index can be set only once. + + + + + + + + + + + + + + + + Cleans up the temporary data sent to the server for dmabuf-based + wl_buffer creation. + + + + + + This request adds one dmabuf to the set in this + zwp_linux_buffer_params_v1. + + The 64-bit unsigned value combined from modifier_hi and modifier_lo + is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the + fb modifier, which is defined in drm_mode.h of Linux UAPI. + This is an opaque token. Drivers use this token to express tiling, + compression, etc. driver-specific modifications to the base format + defined by the DRM fourcc code. + + Starting from version 4, the invalid_format protocol error is sent if + the format + modifier pair was not advertised as supported. + + This request raises the PLANE_IDX error if plane_idx is too large. + The error PLANE_SET is raised if attempting to set a plane that + was already set. + + + + + + + + + + + + + + + + + + This asks for creation of a wl_buffer from the added dmabuf + buffers. The wl_buffer is not created immediately but returned via + the 'created' event if the dmabuf sharing succeeds. The sharing + may fail at runtime for reasons a client cannot predict, in + which case the 'failed' event is triggered. + + The 'format' argument is a DRM_FORMAT code, as defined by the + libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the + authoritative source on how the format codes should work. + + The 'flags' is a bitfield of the flags defined in enum "flags". + 'y_invert' means the that the image needs to be y-flipped. + + Flag 'interlaced' means that the frame in the buffer is not + progressive as usual, but interlaced. An interlaced buffer as + supported here must always contain both top and bottom fields. + The top field always begins on the first pixel row. The temporal + ordering between the two fields is top field first, unless + 'bottom_first' is specified. It is undefined whether 'bottom_first' + is ignored if 'interlaced' is not set. + + This protocol does not convey any information about field rate, + duration, or timing, other than the relative ordering between the + two fields in one buffer. A compositor may have to estimate the + intended field rate from the incoming buffer rate. It is undefined + whether the time of receiving wl_surface.commit with a new buffer + attached, applying the wl_surface state, wl_surface.frame callback + trigger, presentation, or any other point in the compositor cycle + is used to measure the frame or field times. There is no support + for detecting missed or late frames/fields/buffers either, and + there is no support whatsoever for cooperating with interlaced + compositor output. + + The composited image quality resulting from the use of interlaced + buffers is explicitly undefined. A compositor may use elaborate + hardware features or software to deinterlace and create progressive + output frames from a sequence of interlaced input buffers, or it + may produce substandard image quality. However, compositors that + cannot guarantee reasonable image quality in all cases are recommended + to just reject all interlaced buffers. + + Any argument errors, including non-positive width or height, + mismatch between the number of planes and the format, bad + format, bad offset or stride, may be indicated by fatal protocol + errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, + OUT_OF_BOUNDS. + + Dmabuf import errors in the server that are not obvious client + bugs are returned via the 'failed' event as non-fatal. This + allows attempting dmabuf sharing and falling back in the client + if it fails. + + This request can be sent only once in the object's lifetime, after + which the only legal request is destroy. This object should be + destroyed after issuing a 'create' request. Attempting to use this + object after issuing 'create' raises ALREADY_USED protocol error. + + It is not mandatory to issue 'create'. If a client wants to + cancel the buffer creation, it can just destroy this object. + + + + + + + + + + This event indicates that the attempted buffer creation was + successful. It provides the new wl_buffer referencing the dmabuf(s). + + Upon receiving this event, the client should destroy the + zlinux_dmabuf_params object. + + + + + + + This event indicates that the attempted buffer creation has + failed. It usually means that one of the dmabuf constraints + has not been fulfilled. + + Upon receiving this event, the client should destroy the + zlinux_buffer_params object. + + + + + + This asks for immediate creation of a wl_buffer by importing the + added dmabufs. + + In case of import success, no event is sent from the server, and the + wl_buffer is ready to be used by the client. + + Upon import failure, either of the following may happen, as seen fit + by the implementation: + - the client is terminated with one of the following fatal protocol + errors: + - INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS, + in case of argument errors such as mismatch between the number + of planes and the format, bad format, non-positive width or + height, or bad offset or stride. + - INVALID_WL_BUFFER, in case the cause for failure is unknown or + plaform specific. + - the server creates an invalid wl_buffer, marks it as failed and + sends a 'failed' event to the client. The result of using this + invalid wl_buffer as an argument in any request by the client is + defined by the compositor implementation. + + This takes the same arguments as a 'create' request, and obeys the + same restrictions. + + + + + + + + + + + + This object advertises dmabuf parameters feedback. This includes the + preferred devices and the supported formats/modifiers. + + The parameters are sent once when this object is created and whenever they + change. The done event is always sent once after all parameters have been + sent. When a single parameter changes, all parameters are re-sent by the + compositor. + + Compositors can re-send the parameters when the current client buffer + allocations are sub-optimal. Compositors should not re-send the + parameters if re-allocating the buffers would not result in a more optimal + configuration. In particular, compositors should avoid sending the exact + same parameters multiple times in a row. + + The tranche_target_device and tranche_modifier events are grouped by + tranches of preference. For each tranche, a tranche_target_device, one + tranche_flags and one or more tranche_modifier events are sent, followed + by a tranche_done event finishing the list. The tranches are sent in + descending order of preference. All formats and modifiers in the same + tranche have the same preference. + + To send parameters, the compositor sends one main_device event, tranches + (each consisting of one tranche_target_device event, one tranche_flags + event, tranche_modifier events and then a tranche_done event), then one + done event. + + + + + Using this request a client can tell the server that it is not going to + use the wp_linux_dmabuf_feedback object anymore. + + + + + + This event is sent after all parameters of a wp_linux_dmabuf_feedback + object have been sent. + + This allows changes to the wp_linux_dmabuf_feedback parameters to be + seen as atomic, even if they happen via multiple events. + + + + + + This event provides a file descriptor which can be memory-mapped to + access the format and modifier table. + + The table contains a tightly packed array of consecutive format + + modifier pairs. Each pair is 16 bytes wide. It contains a format as a + 32-bit unsigned integer, followed by 4 bytes of unused padding, and a + modifier as a 64-bit unsigned integer. The native endianness is used. + + The client must map the file descriptor in read-only private mode. + + Compositors are not allowed to mutate the table file contents once this + event has been sent. Instead, compositors must create a new, separate + table file and re-send feedback parameters. Compositors are allowed to + store duplicate format + modifier pairs in the table. + + + + + + + + This event advertises the main device that the server prefers to use + when direct scan-out to the target device isn't possible. The + advertised main device may be different for each + wp_linux_dmabuf_feedback object, and may change over time. + + There is exactly one main device. The compositor must send at least + one preference tranche with tranche_target_device equal to main_device. + + Clients need to create buffers that the main device can import and + read from, otherwise creating the dmabuf wl_buffer will fail (see the + wp_linux_buffer_params.create and create_immed requests for details). + The main device will also likely be kept active by the compositor, + so clients can use it instead of waking up another device for power + savings. + + In general the device is a DRM node. The DRM node type (primary vs. + render) is unspecified. Clients must not rely on the compositor sending + a particular node type. Clients cannot check two devices for equality + by comparing the dev_t value. + + If explicit modifiers are not supported and the client performs buffer + allocations on a different device than the main device, then the client + must force the buffer to have a linear layout. + + + + + + + This event splits tranche_target_device and tranche_modifier events in + preference tranches. It is sent after a set of tranche_target_device + and tranche_modifier events; it represents the end of a tranche. The + next tranche will have a lower preference. + + + + + + This event advertises the target device that the server prefers to use + for a buffer created given this tranche. The advertised target device + may be different for each preference tranche, and may change over time. + + There is exactly one target device per tranche. + + The target device may be a scan-out device, for example if the + compositor prefers to directly scan-out a buffer created given this + tranche. The target device may be a rendering device, for example if + the compositor prefers to texture from said buffer. + + The client can use this hint to allocate the buffer in a way that makes + it accessible from the target device, ideally directly. The buffer must + still be accessible from the main device, either through direct import + or through a potentially more expensive fallback path. If the buffer + can't be directly imported from the main device then clients must be + prepared for the compositor changing the tranche priority or making + wl_buffer creation fail (see the wp_linux_buffer_params.create and + create_immed requests for details). + + If the device is a DRM node, the DRM node type (primary vs. render) is + unspecified. Clients must not rely on the compositor sending a + particular node type. Clients cannot check two devices for equality by + comparing the dev_t value. + + This event is tied to a preference tranche, see the tranche_done event. + + + + + + + This event advertises the format + modifier combinations that the + compositor supports. + + It carries an array of indices, each referring to a format + modifier + pair in the last received format table (see the format_table event). + Each index is a 16-bit unsigned integer in native endianness. + + For legacy support, DRM_FORMAT_MOD_INVALID is an allowed modifier. + It indicates that the server can support the format with an implicit + modifier. When a buffer has DRM_FORMAT_MOD_INVALID as its modifier, it + is as if no explicit modifier is specified. The effective modifier + will be derived from the dmabuf. + + A compositor that sends valid modifiers and DRM_FORMAT_MOD_INVALID for + a given format supports both explicit modifiers and implicit modifiers. + + Compositors must not send duplicate format + modifier pairs within the + same tranche or across two different tranches with the same target + device and flags. + + This event is tied to a preference tranche, see the tranche_done event. + + For the definition of the format and modifier codes, see the + wp_linux_buffer_params.create request. + + + + + + + + + + + This event sets tranche-specific flags. + + The scanout flag is a hint that direct scan-out may be attempted by the + compositor on the target device if the client appropriately allocates a + buffer. How to allocate a buffer that can be scanned out on the target + device is implementation-defined. + + This event is tied to a preference tranche, see the tranche_done event. + + + + + + diff --git a/protocols/meson.build b/protocols/meson.build index d400ee4..150ba64 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -15,6 +15,7 @@ wayland_scanner_client = generator( client_protocols = [ 'xdg-shell.xml', + 'linux-dmabuf-unstable-v1.xml', ] client_protos_src = [] diff --git a/src/buffer.c b/src/buffer.c index 73e54c3..64a32f8 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -17,12 +17,18 @@ #include "buffer.h" #include "shm.h" #include "pixels.h" +#include "linux-dmabuf-unstable-v1.h" #include #include #include +#include +#include -extern struct wl_shm* wl_shm; /* Origin: main.c */ +/* Origin: main.c */ +extern struct wl_shm* wl_shm; +extern struct gbm_device* gbm_device; +extern struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1; static void buffer_release(void* data, struct wl_buffer* wl_buffer) { @@ -39,12 +45,16 @@ static const struct wl_buffer_listener buffer_listener = { .release = buffer_release, }; -struct buffer* buffer_create(int width, int height, int stride, uint32_t format) +struct buffer* buffer_create_shm(int width, int height, int stride, + uint32_t format) { + assert(wl_shm); + struct buffer* self = calloc(1, sizeof(*self)); if (!self) return NULL; + self->type = BUFFER_WL_SHM; self->width = width; self->height = height; self->stride = stride; @@ -88,6 +98,60 @@ failure: return NULL; } +struct buffer* buffer_create_dmabuf(int width, int height, uint32_t format) +{ + assert(gbm_device && zwp_linux_dmabuf_v1); + + struct buffer* self = calloc(1, sizeof(*self)); + if (!self) + return NULL; + + self->type = BUFFER_DMABUF; + self->width = width; + self->height = height; + self->format = format; + + self->bo = gbm_bo_create(gbm_device, width, height, format, + GBM_BO_USE_RENDERING); + if (!self->bo) + goto bo_failure; + + struct zwp_linux_buffer_params_v1* params; + params = zwp_linux_dmabuf_v1_create_params(zwp_linux_dmabuf_v1); + if (!params) + goto params_failure; + + uint32_t offset = gbm_bo_get_offset(self->bo, 0); + uint32_t stride = gbm_bo_get_stride(self->bo); + uint64_t mod = gbm_bo_get_modifier(self->bo); + int fd = gbm_bo_get_fd(self->bo); + if (fd < 0) + goto fd_failure; + + zwp_linux_buffer_params_v1_add(params, fd, 0, offset, stride, + mod >> 32, mod & 0xffffffff); + self->wl_buffer = zwp_linux_buffer_params_v1_create_immed(params, width, + height, format, /* flags */ 0); + zwp_linux_buffer_params_v1_destroy(params); + close(fd); + + if (!self->wl_buffer) + goto buffer_failure; + + wl_buffer_add_listener(self->wl_buffer, &buffer_listener, self); + + return self; + +buffer_failure: +fd_failure: + zwp_linux_buffer_params_v1_destroy(params); +params_failure: + gbm_bo_destroy(self->bo); +bo_failure: + free(self); + return NULL; +} + void buffer_destroy(struct buffer* self) { if (!self) @@ -97,6 +161,18 @@ void buffer_destroy(struct buffer* self) self->please_clean_up = true; wl_buffer_destroy(self->wl_buffer); - munmap(self->pixels, self->size); + + switch (self->type) { + case BUFFER_WL_SHM: + munmap(self->pixels, self->size); + break; + case BUFFER_DMABUF: + gbm_bo_destroy(self->bo); + break; + default: + abort(); + break; + } + free(self); } diff --git a/src/main.c b/src/main.c index 6ba3417..c79e688 100644 --- a/src/main.c +++ b/src/main.c @@ -27,7 +27,10 @@ #include #include #include -#include +#include +#include +#include +#include #include "pixman.h" #include "xdg-shell.h" @@ -40,6 +43,7 @@ #include "region.h" #include "buffer.h" #include "renderer.h" +#include "linux-dmabuf-unstable-v1.h" struct window { struct wl_surface* wl_surface; @@ -57,12 +61,16 @@ static struct wl_display* wl_display; static struct wl_registry* wl_registry; struct wl_compositor* wl_compositor = NULL; struct wl_shm* wl_shm = NULL; +struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1 = NULL; +struct gbm_device* gbm_device = NULL; static struct xdg_wm_base* xdg_wm_base; static struct wl_list seats; struct pointer_collection* pointers; struct keyboard_collection* keyboards; +static int drm_fd = -1; static uint32_t shm_format = DRM_FORMAT_INVALID; +static uint32_t dmabuf_format = DRM_FORMAT_INVALID; static bool do_run = true; @@ -100,6 +108,9 @@ static void registry_add(void* data, struct wl_registry* registry, uint32_t id, xdg_wm_base = wl_registry_bind(registry, id, &xdg_wm_base_interface, 1); } else if (strcmp(interface, "wl_shm") == 0) { wl_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); + } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) { + zwp_linux_dmabuf_v1 = wl_registry_bind(registry, id, + &zwp_linux_dmabuf_v1_interface, 2); } else if (strcmp(interface, "wl_seat") == 0) { struct wl_seat* wl_seat; wl_seat = wl_registry_bind(registry, id, &wl_seat_interface, 5); @@ -161,6 +172,32 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = { .ping = xdg_wm_base_ping, }; +static void handle_dmabuf_format(void *data, + struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) +{ + (void)data; + (void)zwp_linux_dmabuf; + + if (dmabuf_format != DRM_FORMAT_INVALID) + return; + + switch (format) { + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_BGRA8888: + dmabuf_format = format; + } +} + +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { + .format = handle_dmabuf_format, +}; + void on_wayland_event(void* obj) { int rc = wl_display_prepare_read(wl_display); @@ -303,8 +340,8 @@ static void window_resize(struct window* w, int width, int height) buffer_destroy(w->front_buffer); buffer_destroy(w->back_buffer); - w->front_buffer = buffer_create(width, height, 4 * width, shm_format); - w->back_buffer = buffer_create(width, height, 4 * width, shm_format); + w->front_buffer = buffer_create_shm(width, height, 4 * width, shm_format); + w->back_buffer = buffer_create_shm(width, height, 4 * width, shm_format); } static void xdg_toplevel_configure(void* data, struct xdg_toplevel* toplevel, @@ -518,6 +555,49 @@ int init_vnc_client_handler(struct vnc_client* client) return rc; } +static int find_render_node(char *node, size_t maxlen) { + bool r = -1; + drmDevice *devices[64]; + + int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0])); + for (int i = 0; i < n; ++i) { + drmDevice *dev = devices[i]; + if (!(dev->available_nodes & (1 << DRM_NODE_RENDER))) + continue; + + strncpy(node, dev->nodes[DRM_NODE_RENDER], maxlen); + node[maxlen - 1] = '\0'; + r = 0; + break; + } + + drmFreeDevices(devices, n); + return r; +} + +static int init_gbm_device(void) +{ + int rc; + + char render_node[256]; + rc = find_render_node(render_node, sizeof(render_node)); + if (rc < 0) + return -1; + + drm_fd = open(render_node, O_RDWR); + if (drm_fd < 0) + return 1; + + gbm_device = gbm_create_device(drm_fd); + if (!gbm_device) { + close(drm_fd); + drm_fd = -1; + return -1; + } + + return 0; +} + static int usage(int r) { fprintf(r ? stderr : stdout, "\ @@ -636,9 +716,21 @@ int main(int argc, char* argv[]) assert(xdg_wm_base); wl_shm_add_listener(wl_shm, &shm_listener, NULL); + wl_display_roundtrip(wl_display); + xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL); wl_display_roundtrip(wl_display); - wl_display_roundtrip(wl_display); + + if (zwp_linux_dmabuf_v1) { + zwp_linux_dmabuf_v1_add_listener(zwp_linux_dmabuf_v1, + &dmabuf_listener, NULL); + wl_display_roundtrip(wl_display); + + if (dmabuf_format == DRM_FORMAT_INVALID || init_gbm_device() < 0) { + zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf_v1); + zwp_linux_dmabuf_v1 = NULL; + } + } struct vnc_client* vnc = vnc_client_create(); if (!vnc) @@ -690,6 +782,12 @@ vnc_failure: wl_compositor_destroy(wl_compositor); wl_shm_destroy(wl_shm); xdg_wm_base_destroy(xdg_wm_base); + if (zwp_linux_dmabuf_v1) + zwp_linux_dmabuf_v1_destroy(zwp_linux_dmabuf_v1); + if (gbm_device) + gbm_device_destroy(gbm_device); + if (drm_fd >= 0) + close(drm_fd); wl_registry_destroy(wl_registry); registry_failure: