/* * Copyright (C) 2023 Mikhail Burakov. This file is part of receiver. * * receiver is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * receiver is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with receiver. If not, see . */ #include "window.h" #include #include #include #include #include #include #include #include "frame.h" #include "linux-dmabuf-unstable-v1.h" #include "util.h" #include "xdg-shell.h" struct Window { struct wl_display* wl_display; struct wl_surface* wl_surface; struct xdg_wm_base* xdg_wm_base; struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1; struct xdg_surface* xdg_surface; struct xdg_toplevel* xdg_toplevel; struct wl_buffer* wl_buffer; }; static void wl_registryDestroy(struct wl_registry** wl_registry) { if (!wl_registry || !*wl_registry) return; wl_registry_destroy(*wl_registry); *wl_registry = NULL; } static void wl_compositorDestroy(struct wl_compositor** wl_compositor) { if (!wl_compositor || !*wl_compositor) return; wl_compositor_destroy(*wl_compositor); *wl_compositor = NULL; } static void xdg_wm_baseDestroy(struct xdg_wm_base** xdg_wm_base) { if (!xdg_wm_base || !*xdg_wm_base) return; xdg_wm_base_destroy(*xdg_wm_base); *xdg_wm_base = NULL; } static void zwp_linux_buffer_params_v1Destroy( struct zwp_linux_buffer_params_v1** zwp_linux_buffer_params_v1) { if (!zwp_linux_buffer_params_v1 || !*zwp_linux_buffer_params_v1) return; zwp_linux_buffer_params_v1_destroy(*zwp_linux_buffer_params_v1); *zwp_linux_buffer_params_v1 = NULL; } static void wl_bufferDestroy(struct wl_buffer** wl_buffer) { if (!wl_buffer || !*wl_buffer) return; wl_buffer_destroy(*wl_buffer); *wl_buffer = NULL; } static void OnWmBasePing(void* data, struct xdg_wm_base* xdg_wm_base, uint32_t serial) { (void)data; xdg_wm_base_pong(xdg_wm_base, serial); } static void OnRegistryGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { struct Window* window = data; if (!strcmp(interface, wl_compositor_interface.name)) { struct AUTO(wl_compositor)* compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version); if (!compositor) { LOG("Failed to bind wayland compositor (%s)", strerror(errno)); return; } window->wl_surface = wl_compositor_create_surface(compositor); if (!window->wl_surface) { LOG("Failed to create wayland surface (%s)", strerror(errno)); return; } } else if (!strcmp(interface, xdg_wm_base_interface.name)) { struct AUTO(xdg_wm_base)* xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, version); if (!xdg_wm_base) { LOG("Failed to bind wayland xdg_wm_base (%s)", strerror(errno)); return; } static const struct xdg_wm_base_listener wm_base_listener = { .ping = OnWmBasePing, }; if (xdg_wm_base_add_listener(xdg_wm_base, &wm_base_listener, NULL)) { LOG("Failed to add wayland wm base listener (%s)", strerror(errno)); return; } window->xdg_wm_base = RELEASE(xdg_wm_base); } else if (!strcmp(interface, zwp_linux_dmabuf_v1_interface.name)) { window->zwp_linux_dmabuf_v1 = wl_registry_bind( registry, name, &zwp_linux_dmabuf_v1_interface, version); if (!window->zwp_linux_dmabuf_v1) LOG("Failed to bind wayland zwp_linux_dmabuf_v1 (%s)", strerror(errno)); } } static void OnRegistryGlobalRemove(void* data, struct wl_registry* registry, uint32_t name) { (void)data; (void)registry; (void)name; } static void OnSurfaceConfigure(void* data, struct xdg_surface* xdg_surface, uint32_t serial) { (void)data; xdg_surface_ack_configure(xdg_surface, serial); } static void OnToplevelConfigure(void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, int32_t height, struct wl_array* states) { (void)data; (void)xdg_toplevel; (void)width; (void)height; (void)states; } static void OnToplevelClose(void* data, struct xdg_toplevel* xdg_toplevel) { (void)data; (void)xdg_toplevel; raise(SIGINT); } static void OnToplevelConfigureBounds(void* data, struct xdg_toplevel* xdg_toplevel, int32_t width, int32_t height) { (void)data; (void)xdg_toplevel; (void)width; (void)height; } static void OnToplevelWmCapabilities(void* data, struct xdg_toplevel* xdg_toplevel, struct wl_array* capabilities) { (void)data; (void)xdg_toplevel; (void)capabilities; } struct Window* WindowCreate(void) { struct AUTO(Window)* window = malloc(sizeof(struct Window)); if (!window) { LOG("Failed to allocate window (%s)", strerror(errno)); return NULL; } *window = (struct Window){ .wl_display = NULL, .wl_surface = NULL, .xdg_wm_base = NULL, .zwp_linux_dmabuf_v1 = NULL, .xdg_surface = NULL, .xdg_toplevel = NULL, .wl_buffer = NULL, }; window->wl_display = wl_display_connect(NULL); if (!window->wl_display) { LOG("Failed to connect wayland display (%s)", strerror(errno)); return NULL; } struct AUTO(wl_registry)* wl_registry = wl_display_get_registry(window->wl_display); if (!wl_registry) { LOG("Failed to get wayland registry (%s)", strerror(errno)); return NULL; } static const struct wl_registry_listener wl_registry_listener = { .global = OnRegistryGlobal, .global_remove = OnRegistryGlobalRemove, }; if (wl_registry_add_listener(wl_registry, &wl_registry_listener, window)) { LOG("Failed to set wayland registry listener (%s)", strerror(errno)); return NULL; } if (wl_display_roundtrip(window->wl_display) == -1) { LOG("Failed to roundtrip wayland display (%s)", strerror(errno)); return NULL; } if (!window->wl_surface || !window->xdg_wm_base || !window->zwp_linux_dmabuf_v1) { LOG("Some wayland objects are missing"); return NULL; } window->xdg_surface = xdg_wm_base_get_xdg_surface(window->xdg_wm_base, window->wl_surface); if (!window->xdg_surface) { LOG("Failed to get wayland surface (%s)", strerror(errno)); return NULL; } static const struct xdg_surface_listener xdg_surface_listener = { .configure = OnSurfaceConfigure, }; if (xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, NULL)) { LOG("Failed to add wayland surface listener (%s)", strerror(errno)); return NULL; } window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); if (!window->xdg_toplevel) { LOG("Failed to get wayland toplevel (%s)", strerror(errno)); return NULL; } static const struct xdg_toplevel_listener xdg_toplevel_listener = { .configure = OnToplevelConfigure, .close = OnToplevelClose, .configure_bounds = OnToplevelConfigureBounds, .wm_capabilities = OnToplevelWmCapabilities, }; if (xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, NULL)) { LOG("Failed to add wayland toplevel listener (%s)", strerror(errno)); return NULL; } xdg_toplevel_set_fullscreen(window->xdg_toplevel, NULL); wl_surface_commit(window->wl_surface); return RELEASE(window); } int WindowGetEventsFd(const struct Window* window) { int events_fd = wl_display_get_fd(window->wl_display); if (events_fd == -1) LOG("Failed to get wayland display fd (%s)", strerror(errno)); return events_fd; } bool WindowProcessEvents(const struct Window* window) { if (wl_display_dispatch(window->wl_display) == -1) { LOG("Failed to dispatch wayland display (%s)", strerror(errno)); return false; } return true; } static void OnZwpLinuxBufferParamsCreated( void* data, struct zwp_linux_buffer_params_v1* zwp_linux_buffer_params_v1, struct wl_buffer* buffer) { (void)zwp_linux_buffer_params_v1; struct wl_buffer** wl_buffer = data; *wl_buffer = buffer; } static void OnZwpLinuxBufferParamsFailed( void* data, struct zwp_linux_buffer_params_v1* zwp_linux_buffer_params_v1) { (void)data; (void)zwp_linux_buffer_params_v1; } bool WindowRenderFrame(struct Window* window, const struct Frame* frame) { struct AUTO(zwp_linux_buffer_params_v1)* zwp_linux_buffer_params_v1 = zwp_linux_dmabuf_v1_create_params(window->zwp_linux_dmabuf_v1); if (!zwp_linux_buffer_params_v1) { LOG("Failed to create wayland dmabuf params (%s)", strerror(errno)); return false; } struct AUTO(wl_buffer)* wl_buffer = NULL; static const struct zwp_linux_buffer_params_v1_listener zwp_linux_buffer_params_v1_listener = { .created = OnZwpLinuxBufferParamsCreated, .failed = OnZwpLinuxBufferParamsFailed, }; if (zwp_linux_buffer_params_v1_add_listener( zwp_linux_buffer_params_v1, &zwp_linux_buffer_params_v1_listener, &wl_buffer)) { LOG("Failed to add buffer wayland dmabuf params listener (%s)", strerror(errno)); return false; } for (size_t i = 0; i < frame->nplanes; i++) { zwp_linux_buffer_params_v1_add( zwp_linux_buffer_params_v1, frame->planes[i].dmabuf_fd, (uint32_t)i, frame->planes[i].offset, frame->planes[i].pitch, frame->planes[i].modifier >> 32, frame->planes[i].modifier & UINT32_MAX); } zwp_linux_buffer_params_v1_create(zwp_linux_buffer_params_v1, (int)frame->width, (int)frame->height, frame->fourcc, 0); if (wl_display_roundtrip(window->wl_display) == -1) { LOG("Failed to roundtrip wayland display (%s)", strerror(errno)); return false; } if (!wl_buffer) { LOG("Failed to create wl_buffer"); return false; } SWAP(window->wl_buffer, wl_buffer); wl_surface_attach(window->wl_surface, window->wl_buffer, 0, 0); wl_surface_damage(window->wl_surface, 0, 0, INT32_MAX, INT32_MAX); wl_surface_commit(window->wl_surface); if (wl_display_roundtrip(window->wl_display) == -1) { LOG("Failed to roundtrip wayland display (%s)", strerror(errno)); return false; } return true; } void WindowDestroy(struct Window** window) { if (!window || !*window) return; if ((*window)->xdg_toplevel) xdg_toplevel_destroy((*window)->xdg_toplevel); if ((*window)->xdg_surface) xdg_surface_destroy((*window)->xdg_surface); if ((*window)->zwp_linux_dmabuf_v1) zwp_linux_dmabuf_v1_destroy((*window)->zwp_linux_dmabuf_v1); if ((*window)->xdg_wm_base) xdg_wm_base_destroy((*window)->xdg_wm_base); if ((*window)->wl_surface) wl_surface_destroy((*window)->wl_surface); if ((*window)->wl_display) wl_display_disconnect((*window)->wl_display); free(*window); *window = NULL; }