diff --git a/CMakeLists.txt b/CMakeLists.txt index 534614f..1ec1ed8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -261,6 +261,7 @@ if(MW_USE_WAYLAND) scan_wayland_protocol("unstable" "primary-selection" "-unstable-v1") scan_wayland_protocol("unstable" "pointer-constraints" "-unstable-v1") scan_wayland_protocol("unstable" "relative-pointer" "-unstable-v1") + scan_wayland_protocol("staging" "fifo" "-v1") scan_wayland_protocol_from_file("wlr-layer-shell" "wlr-layer-shell-unstable-v1.xml") target_sources( diff --git a/examples/vkdemos/vulkan.c b/examples/vkdemos/vulkan.c index 9060a97..6015415 100644 --- a/examples/vkdemos/vulkan.c +++ b/examples/vkdemos/vulkan.c @@ -257,8 +257,8 @@ void vulkan_setup(MwWidget handle) { swapchainCreateInfo.imageExtent = (VkExtent2D){.width = ow, .height = oh}, swapchainCreateInfo.imageArrayLayers = 1, swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT; + VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_SAMPLED_BIT; // th is how we specify no transformation. swapchainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; diff --git a/include/Mw/LowLevel/Wayland.h b/include/Mw/LowLevel/Wayland.h index f7102ce..5e43c34 100644 --- a/include/Mw/LowLevel/Wayland.h +++ b/include/Mw/LowLevel/Wayland.h @@ -207,6 +207,7 @@ MwInline int wayland_load_funcs() { #include "Wayland/relative-pointer-client-protocol.h" #include "Wayland/xdg-toplevel-icon-client-protocol.h" #include "Wayland/wlr-layer-shell-client-protocol.h" +#include "Wayland/fifo-client-protocol.h" #endif typedef struct wayland_protocol { @@ -266,6 +267,7 @@ struct _MwLLWaylandShmBuffer { struct wl_buffer* shm_buffer_back; struct wl_surface* surface; struct wl_output* output; + struct wp_fifo_v1* fifo; MwU8* buf; MwU8* buf_back; @@ -435,6 +437,8 @@ struct _MwLLWayland { struct _MwLLWaylandShmBuffer backbuffer; struct _MwLLWaylandShmBuffer cursor; struct _MwLLWaylandShmBuffer* icon; + MwBool fifo_wait; + MwBool disable_fifo; MwLLPixmap icon_pixmap; @@ -472,6 +476,17 @@ void MwLLWaylandBackbufferSetup(struct _MwLLWayland* wayland); /* Destroy the backbuffer */ void MwLLWaylandBackbufferDestroy(struct _MwLLWayland* handle); +/* Bind a wp_fifo_v1 object to `buffer`'s surface, if wp_fifo_manager_v1 is available and it doesn't have one already. */ +void MwLLWaylandFifoSurfaceSetup(struct _MwLLWayland* wayland, struct _MwLLWaylandShmBuffer* buffer); +/* Destroy `buffer`'s wp_fifo_v1 object, if any. */ +void MwLLWaylandFifoSurfaceDestroy(struct _MwLLWaylandShmBuffer* buffer); +/* Commit `buffer`'s surface, constraining the update to the next display refresh via wp_fifo_v1 if bound. */ +void MwLLWaylandFifoCommit(struct _MwLLWaylandShmBuffer* buffer); + +#ifdef MW_VULKAN +#warning Wayland backend currently disabled by default when Vulkan enabled. Use with caution (MW_BACKEND=wayland to override). +#endif + void MwLLWaylandBufferSetup(struct _MwLLWaylandShmBuffer* buffer, MwU32 width, MwU32 height); void MwLLWaylandBufferUpdate(MwLL self, struct _MwLLWaylandShmBuffer* buffer); void MwLLWaylandBufferDestroy(struct _MwLLWaylandShmBuffer* buffer); diff --git a/pl/rules.pl b/pl/rules.pl index 986ee98..6c4df63 100644 --- a/pl/rules.pl +++ b/pl/rules.pl @@ -66,6 +66,7 @@ if (grep(/^wayland$/, @backends)) { scan_wayland_protocol("unstable", "primary-selection", "-unstable-v1"); scan_wayland_protocol("unstable", "pointer-constraints", "-unstable-v1"); scan_wayland_protocol("unstable", "relative-pointer", "-unstable-v1"); + scan_wayland_protocol("staging", "fifo", "-v1"); scan_wayland_protocol_from_file("wlr-layer-shell", "wlr-layer-shell-unstable-v1.xml"); $gl_libs = "-lGL -lGLU"; diff --git a/src/backend/wayland/buffer.c b/src/backend/wayland/buffer.c index df1de79..3ee5e3c 100644 --- a/src/backend/wayland/buffer.c +++ b/src/backend/wayland/buffer.c @@ -1,5 +1,39 @@ #include #include +#include "../../../external/stb_ds.h" + +/* Bind a wp_fifo_v1 object to `buffer`'s surface, if wp_fifo_manager_v1 is available and it doesn't have one already. */ +void MwLLWaylandFifoSurfaceSetup(struct _MwLLWayland* wayland, struct _MwLLWaylandShmBuffer* buffer) { + wayland_protocol_t* fifo_manager; + + if(buffer->fifo || !buffer->surface) { + return; + } + + fifo_manager = shget(wayland->wl_protocol_map, wp_fifo_manager_v1_interface.name); + if(!fifo_manager) { + return; + } + + buffer->fifo = wp_fifo_manager_v1_get_fifo(fifo_manager->context, buffer->surface); +} + +/* Destroy `buffer`'s wp_fifo_v1 object, if any. */ +void MwLLWaylandFifoSurfaceDestroy(struct _MwLLWaylandShmBuffer* buffer) { + if(buffer->fifo) { + wp_fifo_v1_destroy(buffer->fifo); + buffer->fifo = NULL; + } +} + +/* Commit `buffer`'s surface, constraining the update to the next display refresh via wp_fifo_v1 if bound. */ +void MwLLWaylandFifoCommit(struct _MwLLWaylandShmBuffer* buffer) { + if(buffer->fifo) { + wp_fifo_v1_set_barrier(buffer->fifo); + wp_fifo_v1_wait_barrier(buffer->fifo); + } + wl_surface_commit(buffer->surface); +} void MwLLWaylandFramebufferSetup(struct _MwLLWayland* wayland) { MwLLWaylandBufferSetup(&wayland->framebuffer, wayland->ww, wayland->wh); diff --git a/src/backend/wayland/interfaces.c b/src/backend/wayland/interfaces.c index 0d960c2..9ce4261 100644 --- a/src/backend/wayland/interfaces.c +++ b/src/backend/wayland/interfaces.c @@ -19,6 +19,35 @@ static void setup_zwp_clipboard(MwLL self, struct wl_seat* wl_seat); static void destroy_clipboard(MwLL self, struct wl_seat* wl_seat); static void destroy_zwp_clipboard(MwLL self, struct wl_seat* wl_seat); +/* Recursively dispatch a key event to a widget and its children */ +static void recursive_dispatch_key(MwLL handle, int* k) { + MwWidget h = (MwWidget)handle->common.user; + MwLLDispatch(handle, key, k); + if(h) { + int i; + for(i = 0; i < arrlen(h->children); i++) { + MwLLDispatch(h->children[i]->lowlevel, key, k); + if(arrlen(h->children[i]->children) > 0) { + recursive_dispatch_key(h->children[i]->lowlevel, k); + } + } + } +}; +/* Recursively dispatch a key released event to a widget and its children */ +static void recursive_dispatch_key_released(MwLL handle, int* k) { + MwWidget h = (MwWidget)handle->common.user; + MwLLDispatch(handle, key_released, k); + if(h) { + int i; + for(i = 0; i < arrlen(h->children); i++) { + MwLLDispatch(h->children[i]->lowlevel, key_released, k); + if(arrlen(h->children[i]->children) > 0) { + recursive_dispatch_key_released(h->children[i]->lowlevel, k); + } + } + } +}; + /* Recursively dispatch a move event to a widget and its children */ static void recursive_dispatch_move(MwLL handle, MwMouse* p) { MwWidget h = (MwWidget)handle->common.user; @@ -923,15 +952,11 @@ static void keyboard_key(void* data, if(key != -1) { if(state == WL_KEYBOARD_KEY_STATE_PRESSED) { - MwLL topmost_parent = self; - while(topmost_parent->wayland.parent) { - MwLLDispatch(topmost_parent, key, &key); - topmost_parent = topmost_parent->wayland.parent; - } + recursive_dispatch_key(self, &key); self->wayland.holding_key = MwTRUE; self->wayland.last_pressed_key = key; } else { - MwLLDispatch(self, key_released, &key); + recursive_dispatch_key_released(self, &key); self->wayland.holding_key = MwFALSE; } self->wayland.start_time = MwTimeGetTick(); @@ -1193,6 +1218,24 @@ static void wp_viewporter_interface_destroy(struct _MwLLWayland* wayland, waylan (void)data; } +/* wp_fifo_manager_v1 setup function */ +static wayland_protocol_t* wp_fifo_manager_v1_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) { + wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t)); + (void)version; + proto->listener = NULL; + proto->context = wl_registry_bind(wayland->registry, name, &wp_fifo_manager_v1_interface, 1); + + return proto; +} + +static void wp_fifo_manager_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) { + (void)wayland; + if(data->context) { + wp_fifo_manager_v1_destroy(data->context); + } + free(data); +} + /* zxdg_decoration_manager_v1 setup function */ static wayland_protocol_t* zxdg_decoration_manager_v1_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) { wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t)); @@ -1295,6 +1338,7 @@ void MwLLWaylandSetupCallbacks(struct _MwLLWayland* wayland) { WL_INTERFACE(zwp_pointer_constraints_v1); WL_INTERFACE(zwp_relative_pointer_manager_v1); WL_INTERFACE(xdg_wm_base); + WL_INTERFACE(wp_fifo_manager_v1); WL_INTERFACE(zwlr_layer_shell_v1); /* Only used for layer surface, but we use it always if it's turned into a tool window */ if(wayland->type == MwLL_WAYLAND_TOPLEVEL) { WL_INTERFACE(wp_viewporter); diff --git a/src/backend/wayland/wayland.c b/src/backend/wayland/wayland.c index 4f832dc..433001b 100644 --- a/src/backend/wayland/wayland.c +++ b/src/backend/wayland/wayland.c @@ -242,8 +242,7 @@ static void xdg_surface_configure( xdg_surface_ack_configure(xdg_surface, serial); - MwLLWaylandCascadeChildren(self); - // MwLLDispatch(self, draw, NULL); + MwLLDispatch(self, draw, NULL); if(self->wayland.configured) { MwLLWaylandBufferUpdate(self, &self->wayland.framebuffer); @@ -260,7 +259,11 @@ void MwLLWaylandBufferUpdate(MwLL self, struct _MwLLWaylandShmBuffer* buffer) { // Yes this is needed every time, it's how we fix weston. if(self->wayland.configured) wl_surface_attach(buffer->surface, buffer->shm_buffer, 0, 0); - wl_surface_commit(buffer->surface); + if(self->wayland.fifo_wait) { + MwLLWaylandFifoCommit(buffer); + } else { + wl_surface_commit(buffer->surface); + } } } } @@ -309,7 +312,9 @@ static void setup_toplevel(MwLL r, int x, int y) { /* Create a wl_surface, a xdg_surface and a xdg_toplevel */ r->wayland.framebuffer.surface = wl_compositor_create_surface(r->wayland.compositor); r->wayland.backbuffer.surface = wl_compositor_create_surface(r->wayland.compositor); - r->wayland.toplevel->ssurface = wl_subcompositor_get_subsurface(r->wayland.toplevel->scompositor, r->wayland.framebuffer.surface, r->wayland.backbuffer.surface); + MwLLWaylandFifoSurfaceSetup(&r->wayland, &r->wayland.framebuffer); + MwLLWaylandFifoSurfaceSetup(&r->wayland, &r->wayland.backbuffer); + r->wayland.toplevel->ssurface = wl_subcompositor_get_subsurface(r->wayland.toplevel->scompositor, r->wayland.framebuffer.surface, r->wayland.backbuffer.surface); wl_subsurface_set_desync(r->wayland.toplevel->ssurface); r->wayland.toplevel->xdg_surface = @@ -384,6 +389,8 @@ static void destroy_toplevel(MwLL r) { wl_pointer_destroy(r->wayland.pointer); wl_keyboard_destroy(r->wayland.keyboard); + MwLLWaylandFifoSurfaceDestroy(&r->wayland.framebuffer); + MwLLWaylandFifoSurfaceDestroy(&r->wayland.backbuffer); wl_surface_destroy(r->wayland.framebuffer.surface); free(r->wayland.toplevel); @@ -422,6 +429,7 @@ static void setup_sublevel(MwLL parent, MwLL r, int x, int y) { } r->wayland.framebuffer.surface = wl_compositor_create_surface(compositor); + MwLLWaylandFifoSurfaceSetup(&r->wayland, &r->wayland.framebuffer); r->wayland.xkb_keymap = parent->wayland.xkb_keymap; r->wayland.xkb_state = parent->wayland.xkb_state; @@ -546,6 +554,7 @@ static void setup_popup(MwLL r, int x, int y, MwLL parent) { r->wayland.xkb_state = topmost_parent->wayland.xkb_state; r->wayland.framebuffer.surface = wl_compositor_create_surface(r->wayland.compositor); + MwLLWaylandFifoSurfaceSetup(&r->wayland, &r->wayland.framebuffer); r->wayland.popup->xdg_surface = xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.framebuffer.surface); @@ -587,6 +596,8 @@ static void destroy_popup(MwLL r) { xdg_positioner_destroy(r->wayland.popup->xdg_positioner); + MwLLWaylandFifoSurfaceDestroy(&r->wayland.framebuffer); + wl_surface_destroy(r->wayland.framebuffer.surface); wl_registry_destroy(r->wayland.registry); @@ -670,6 +681,7 @@ static void setup_layer_surface(MwLL r, int x, int y, int width, int height) { r->wayland.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); r->wayland.framebuffer.surface = wl_compositor_create_surface(r->wayland.compositor); + MwLLWaylandFifoSurfaceSetup(&r->wayland, &r->wayland.framebuffer); r->wayland.layer_surface->surface = zwlr_layer_shell_v1_get_layer_surface(layer_shell, r->wayland.framebuffer.surface, NULL, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "milsko-surfaces"); @@ -700,6 +712,8 @@ static void destroy_layer_surface(MwLL r) { zxdg_decoration_manager_v1_destroy(dec->manager); } + MwLLWaylandFifoSurfaceDestroy(&r->wayland.framebuffer); + wl_surface_destroy(r->wayland.framebuffer.surface); wl_compositor_destroy(r->wayland.compositor); @@ -1128,8 +1142,7 @@ static void MwLLSetXYImpl(MwLL handle, int x, int y) { if(handle->wayland.type != MwLL_WAYLAND_TOPLEVEL) recursive_render(handle); - MwLLWaylandCascadeChildren(handle); - // MwLLDispatch(handle, draw, NULL); + MwLLDispatch(handle, draw, NULL); } static void actually_set_wh(MwLL handle) { @@ -1158,8 +1171,7 @@ static void actually_set_wh(MwLL handle) { MwLLWaylandFramebufferSetup(&handle->wayland); MwLLWaylandBackbufferDestroy(&handle->wayland); MwLLWaylandBackbufferSetup(&handle->wayland); - MwLLWaylandCascadeChildren(handle); - // MwLLDispatch(handle, draw, NULL); + MwLLDispatch(handle, draw, NULL); } static void MwLLSetWHImpl(MwLL handle, int w, int h) { @@ -1444,8 +1456,7 @@ static int MwLLPendingImpl(MwLL handle) { if(handle->wayland.dark_theme_detection) { handle->wayland.dark_theme_detection = MwFALSE; detect_dark_theme(handle); - MwLLWaylandCascadeChildren(handle); - // MwLLDispatch(handle, draw, NULL); + MwLLDispatch(handle, draw, NULL); } else { MwLLDBusPortalPoll(&wl_call_tbl.dbus, &handle->wayland.dbus, handle, "org.freedesktop.portal.Settings", "org.freedesktop.appearance", "color-scheme", &dark_theme_listener); } @@ -1474,10 +1485,12 @@ static int MwLLPendingImpl(MwLL handle) { return pending; } + if(!handle->wayland.disable_fifo) handle->wayland.fifo_wait = MwTRUE; MwLLWaylandBufferUpdate(handle, &handle->wayland.framebuffer); if(handle->wayland.type == MwLL_WAYLAND_TOPLEVEL) { MwLLWaylandBufferUpdate(handle, &handle->wayland.backbuffer); } + if(!handle->wayland.disable_fifo) handle->wayland.fifo_wait = MwFALSE; return handle->wayland.force_render || handle->wayland.events_pending || pending; } @@ -1494,9 +1507,11 @@ static void MwLLNextEventImpl(MwLL handle) { } if(handle->wayland.force_render) { + handle->wayland.fifo_wait = MwTRUE; MwLLDispatch(handle, draw, NULL); if(handle->wayland.configured) MwLLWaylandBufferUpdate(handle, &handle->wayland.framebuffer); handle->wayland.force_render = 0; + handle->wayland.fifo_wait = MwFALSE; } if(handle->wayland.events_pending) { handle->wayland.events_pending = 0; @@ -1904,7 +1919,7 @@ static void MwLLSetDarkThemeImpl(MwLL handle, int toggle) { (void)toggle; /* Not Really what's supposed to happen with this function, but take this opprutunity to do the handlers that will force everything to redraw. */ - MwLLWaylandCascadeChildren(handle); + MwLLDispatch(handle, draw, NULL); // MwLLDispatch(handle, resize, NULL); MwLLDispatch(handle, draw, NULL); } @@ -1958,10 +1973,14 @@ static int MwLLWaylandCallInitImpl(void) { } else { loadWayland |= (strcmp(mw_backend_env, "wayland") == 0); } + } - } else if(getenv("WAYLAND_DISPLAY")) { + /* NOTE: when vulkan is enabled, refuse to default to wayland. Remove this if I ever get Vulkan widget working with the new surfaces system. */ +#ifndef MW_VULKAN + else if(getenv("WAYLAND_DISPLAY")) { loadWayland |= (getenv("WAYLAND_DISPLAY") != NULL); } +#endif #ifdef MW_OPENGL if(getenv("WSLENV") || getenv("WSL_DISTRO_NAME")) { diff --git a/src/widget/opengl.c b/src/widget/opengl.c index 30a1cee..9e1b91e 100644 --- a/src/widget/opengl.c +++ b/src/widget/opengl.c @@ -240,6 +240,12 @@ static int wcreate(MwWidget handle) { waylandopengl_t* o = r = malloc(sizeof(*o)); int gbm_format = 0; EGLConfig egl_configs[1024]; + MwWidget topmost_parent = handle; + + /* Disable fifo waiting when we have an OpenGL widget */ + while(topmost_parent->parent) topmost_parent = topmost_parent->parent; + topmost_parent->lowlevel->wayland.disable_fifo = MwTRUE; + memset(o, 0, sizeof(waylandopengl_t)); o->gllib = MwDynamicOpen("libEGL.so"); diff --git a/src/widget/vulkan.c b/src/widget/vulkan.c index f4c5340..acffda3 100644 --- a/src/widget/vulkan.c +++ b/src/widget/vulkan.c @@ -240,6 +240,8 @@ static int vulkan_instance_setup(MwWidget handle, vulkan_t* o) { #endif #ifdef USE_WAYLAND if(handle->lowlevel->common.type == MwLLBackendWayland) { + MwWidget topmost_parent = handle; + arrput(o->enabledExtensions, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME); /* take this opprutunity to set the widget to always render */ MwWaylandVulkan = MwTRUE;