From 3cf94f66513a5049f913032f0b927a3bc1b2ae31 Mon Sep 17 00:00:00 2001 From: IoI_xD Date: Thu, 19 Mar 2026 19:43:57 -0700 Subject: [PATCH] wayland: malicious compliance with gnome's CSD rule --- .clangd | 2 + include/Mw/LowLevel/Wayland.h | 26 ++++- pl/rules.pl | 1 + src/backend/wayland.c | 183 ++++++++++++++++++++++++---------- 4 files changed, 160 insertions(+), 52 deletions(-) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..c2520c5 --- /dev/null +++ b/.clangd @@ -0,0 +1,2 @@ +Completion: + HeaderInsertion: Never diff --git a/include/Mw/LowLevel/Wayland.h b/include/Mw/LowLevel/Wayland.h index 76a5801..b292749 100644 --- a/include/Mw/LowLevel/Wayland.h +++ b/include/Mw/LowLevel/Wayland.h @@ -24,12 +24,21 @@ MWDECL int MwLLWaylandCallInit(void); #ifndef WL_PROTOCOLS_DEFINED #define WL_PROTOCOLS_DEFINED #include "Wayland/xdg-shell-client-protocol.h" +#include "Wayland/viewporter-client-protocol.h" #include "Wayland/xdg-decoration-client-protocol.h" #include "Wayland/cursor-shape-client-protocol.h" #include "Wayland/primary-selection-client-protocol.h" #include "Wayland/xdg-toplevel-icon-client-protocol.h" #endif +#define SSD_BORDER_FRAME_LEFT 5 +#define SSD_BORDER_FRAME_RIGHT 5 +#define SSD_BORDER_FRAME_TOP 24 +#define SSD_BORDER_FRAME_BOTTOM 5 + +#define SSD_LEFT_OFFSET(handle) handle->wayland.has_decorations ? 0 : SSD_BORDER_FRAME_LEFT +#define SSD_TOP_OFFSET(handle) handle->wayland.has_decorations ? 0 : SSD_BORDER_FRAME_TOP + struct _MwLLWayland; typedef struct wayland_protocol { @@ -58,6 +67,9 @@ struct _MwLLWaylandTopLevel { MwBool compositor_created; MwBool xdg_wm_base_created; MwBool xdg_surface_created; + + struct wl_subsurface* ssurface; + struct wl_subcompositor* scompositor; }; struct _MwLLWaylandSublevel { @@ -143,6 +155,9 @@ struct _MwLLWayland { MwBool always_render; + MwBool has_decorations; + char title[255]; + struct wl_display* display; struct wl_registry* registry; struct wl_compositor* compositor; @@ -151,6 +166,8 @@ struct _MwLLWayland { struct wl_region* region; struct wl_output* output; + struct wp_viewport* vp; + /* clipboard related stuff. * Note that unlike most interfaces, we don't keep zwp_primary_selection stuff in a wayland_protocol_t because we use wl_data_device as a fallback and want to have it share memory space.*/ @@ -192,6 +209,7 @@ struct _MwLLWayland { MwBool did_event_loop_early; struct _MwLLWaylandShmBuffer framebuffer; + struct _MwLLWaylandShmBuffer backbuffer; struct _MwLLWaylandShmBuffer cursor; struct _MwLLWaylandShmBuffer* icon; @@ -203,12 +221,16 @@ struct _MwLLWayland { uint32_t last_time; + MwBool moving; + MwLL currentlyHeldWidget; struct wl_surface* curSurface; - cairo_surface_t* cs; - cairo_t* cairo; + cairo_surface_t* front_cs; + cairo_surface_t* back_cs; + cairo_t* front_cairo; + cairo_t* back_cairo; }; struct _MwLLWaylandColor { diff --git a/pl/rules.pl b/pl/rules.pl index 45bb6da..a35a6c3 100644 --- a/pl/rules.pl +++ b/pl/rules.pl @@ -37,6 +37,7 @@ if (grep(/^wayland$/, @backends)) { } scan_wayland_protocol("stable", "xdg-shell", ""); + scan_wayland_protocol("stable", "viewporter", ""); scan_wayland_protocol("stable", "tablet", "-v2"); scan_wayland_protocol("staging", "xdg-toplevel-icon", "-v1"); scan_wayland_protocol("staging", "cursor-shape", "-v1"); diff --git a/src/backend/wayland.c b/src/backend/wayland.c index 8cc6000..40dfbc7 100644 --- a/src/backend/wayland.c +++ b/src/backend/wayland.c @@ -1,19 +1,9 @@ #include -#include -#include -#include -#include #include -#include -#include +#include #include "../../external/stb_ds.h" -#include "Mw/BaseTypes.h" - -#include -#include -#include /* TODO: * - MwLLMakePopupImpl @@ -49,6 +39,11 @@ static void framebuffer_setup(struct _MwLLWayland* wayland); /* Destroy the framebuffer */ static void framebuffer_destroy(struct _MwLLWayland* handle); +/* Setup the framebuffer with the saved width/height */ +static void backbuffer_setup(struct _MwLLWayland* wayland); +/* Destroy the backbuffer */ +static void backbuffer_destroy(struct _MwLLWayland* handle); + static void buffer_destroy(struct _MwLLWaylandShmBuffer* buffer); static void region_setup(MwLL handle); @@ -435,7 +430,7 @@ static void pointer_button(void* data, struct wl_pointer* wl_pointer, MwU32 seri p.point = self->wayland.cur_mouse_pos; - if(self->wayland.framebuffer.surface == self->wayland.curSurface) { + if(self->wayland.framebuffer.surface == self->wayland.curSurface || self->wayland.backbuffer.surface == self->wayland.curSurface) { int i; switch(button) { @@ -798,7 +793,11 @@ static void wl_compositor_interface_destroy(struct _MwLLWayland* wayland, waylan /* wl_subcompositor setup function */ static wayland_protocol_t* wl_subcompositor_setup(MwU32 name, struct _MwLLWayland* wayland) { - wayland->sublevel->subcompositor = wl_registry_bind(wayland->registry, name, &wl_subcompositor_interface, 1); + if(wayland->type == MWLL_WAYLAND_TOPLEVEL) { + wayland->toplevel->scompositor = wl_registry_bind(wayland->registry, name, &wl_subcompositor_interface, 1); + } else { + wayland->sublevel->subcompositor = wl_registry_bind(wayland->registry, name, &wl_subcompositor_interface, 1); + } return NULL; } @@ -824,6 +823,18 @@ static void xdg_wm_base_interface_destroy(struct _MwLLWayland* wayland, wayland_ free(data->listener); } +/* xdg_wm_base setup function */ +static wayland_protocol_t* wp_viewporter_setup(MwU32 name, struct _MwLLWayland* wayland) { + wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t)); + proto->context = wl_registry_bind(wayland->registry, name, &wp_viewporter_interface, 1); + + return proto; +} + +static void wp_viewporter_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) { + free(data->listener); +} + /* the two decoration manager constructs */ typedef struct zxdg_decoration_manager_v1_context { struct zxdg_decoration_manager_v1* manager; @@ -875,6 +886,9 @@ static void xdg_toplevel_configure(void* data, xdg_surface_set_window_geometry(self->wayland.toplevel->xdg_surface, 0, 0, self->wayland.ww, self->wayland.wh); + backbuffer_destroy(&self->wayland); + backbuffer_setup(&self->wayland); + framebuffer_destroy(&self->wayland); framebuffer_setup(&self->wayland); region_setup(self); @@ -910,6 +924,7 @@ static void xdg_surface_configure( if(self->wayland.configured) { wl_surface_commit(self->wayland.framebuffer.surface); + wl_surface_commit(self->wayland.backbuffer.surface); } self->wayland.configured = MwTRUE; @@ -917,8 +932,8 @@ static void xdg_surface_configure( /* wl_shm setup function */ static wayland_protocol_t* wl_shm_setup(MwU32 name, struct _MwLLWayland* wayland) { - wayland->framebuffer.shm = wl_registry_bind(wayland->registry, name, &wl_shm_interface, 1); + wayland->backbuffer.shm = wl_registry_bind(wayland->registry, name, &wl_shm_interface, 1); wayland->cursor.shm = wl_registry_bind(wayland->registry, name, &wl_shm_interface, 1); return NULL; @@ -979,16 +994,34 @@ static void buffer_destroy(struct _MwLLWaylandShmBuffer* buffer) { static void framebuffer_setup(struct _MwLLWayland* wayland) { buffer_setup(&wayland->framebuffer, wayland->ww, wayland->wh); - wayland->cs = cairo_image_surface_create_for_data(wayland->framebuffer.buf, CAIRO_FORMAT_ARGB32, wayland->ww, wayland->wh, 4 * wayland->ww); - wayland->cairo = cairo_create(wayland->cs); + wayland->front_cs = cairo_image_surface_create_for_data(wayland->framebuffer.buf, CAIRO_FORMAT_ARGB32, wayland->ww, wayland->wh, 4 * wayland->ww); + wayland->front_cairo = cairo_create(wayland->front_cs); memset(wayland->framebuffer.buf, 255, wayland->framebuffer.buf_size); if(wayland->configured) update_buffer(&wayland->framebuffer); }; static void framebuffer_destroy(struct _MwLLWayland* wayland) { buffer_destroy(&wayland->framebuffer); - cairo_destroy(wayland->cairo); - cairo_surface_destroy(wayland->cs); + cairo_destroy(wayland->front_cairo); + cairo_surface_destroy(wayland->front_cs); +}; + +static void backbuffer_setup(struct _MwLLWayland* wayland) { + if(wayland->type != MWLL_WAYLAND_TOPLEVEL) { + return; + } + buffer_setup(&wayland->backbuffer, wayland->ww, wayland->wh); + + wayland->back_cs = cairo_image_surface_create_for_data(wayland->backbuffer.buf, CAIRO_FORMAT_ARGB32, wayland->ww, wayland->wh, 4 * wayland->ww); + wayland->back_cairo = cairo_create(wayland->back_cs); + + memset(wayland->backbuffer.buf, 255, wayland->backbuffer.buf_size); + if(wayland->configured) update_buffer(&wayland->backbuffer); +}; +static void backbuffer_destroy(struct _MwLLWayland* wayland) { + buffer_destroy(&wayland->backbuffer); + cairo_destroy(wayland->back_cairo); + cairo_surface_destroy(wayland->back_cs); }; static void region_invalidate(MwLL handle) { @@ -1007,7 +1040,6 @@ static void region_setup(MwLL handle) { } wl_region_add(handle->wayland.o_region, 0, 0, 1, 1); - wl_surface_set_opaque_region(handle->wayland.framebuffer.surface, handle->wayland.o_region); if(handle->wayland.type == MWLL_WAYLAND_POPUP) { wl_region_add(handle->wayland.region, 0, 0, width + abs(handle->wayland.x), height + abs(handle->wayland.y)); @@ -1022,7 +1054,12 @@ static void region_setup(MwLL handle) { } wl_region_add(handle->wayland.region, 0, 0, width, height); } + wl_surface_set_opaque_region(handle->wayland.framebuffer.surface, handle->wayland.region); wl_surface_set_input_region(handle->wayland.framebuffer.surface, handle->wayland.region); + if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) { + wl_surface_set_opaque_region(handle->wayland.backbuffer.surface, handle->wayland.region); + wl_surface_set_input_region(handle->wayland.backbuffer.surface, handle->wayland.region); + } } static wayland_protocol_t* xdg_toplevel_icon_manager_v1_setup(MwU32 name, struct _MwLLWayland* wayland) { @@ -1076,6 +1113,8 @@ static int event_loop(MwLL handle) { wl_display_cancel_read(wayland->display); wl_surface_commit(wayland->framebuffer.surface); + if(wayland->type == MWLL_WAYLAND_TOPLEVEL) + wl_surface_commit(wayland->backbuffer.surface); return 0; } wl_display_read_events(wayland->display); @@ -1109,8 +1148,10 @@ static void setup_callbacks(struct _MwLLWayland* wayland) { WL_INTERFACE(zwp_primary_selection_device_manager_v1); if(wayland->type == MWLL_WAYLAND_TOPLEVEL) { WL_INTERFACE(xdg_wm_base); + WL_INTERFACE(wp_viewporter); WL_INTERFACE(zxdg_decoration_manager_v1); WL_INTERFACE(xdg_toplevel_icon_manager_v1); + WL_INTERFACE(wl_subcompositor); } else if(wayland->type == MWLL_WAYLAND_POPUP) { WL_INTERFACE(xdg_wm_base); } else { @@ -1138,8 +1179,6 @@ static void setup_toplevel(MwLL r, int x, int y) { return; } - r->wayland.framebuffer.surface = NULL; - /* Do a roundtrip to ensure all interfaces are setup. */ r->wayland.registry = wl_display_get_registry(r->wayland.display); wl_registry_add_listener(r->wayland.registry, &r->wayland.registry_listener, r); @@ -1153,9 +1192,11 @@ 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); r->wayland.toplevel->xdg_surface = - xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.framebuffer.surface); + xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.backbuffer.surface); r->wayland.toplevel->xdg_top_level = xdg_surface_get_toplevel(r->wayland.toplevel->xdg_surface); /* setup mandatory listeners */ @@ -1172,6 +1213,7 @@ static void setup_toplevel(MwLL r, int x, int y) { xdg_toplevel_set_app_id(r->wayland.toplevel->xdg_top_level, "MilskoWaylandApp"); /* Perform the initial commit and wait for the first configure event */ + wl_surface_commit(r->wayland.backbuffer.surface); wl_surface_commit(r->wayland.framebuffer.surface); while(!r->wayland.configured) { event_loop(r); @@ -1186,8 +1228,18 @@ static void setup_toplevel(MwLL r, int x, int y) { zxdg_toplevel_decoration_v1_set_mode( dec->decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + + r->wayland.has_decorations = MwTRUE; } else { - printf("zxdg null\n"); + /* otherwise set up viewporter */ + struct wp_viewporter* wp = WAYLAND_GET_INTERFACE(r->wayland, wp_viewporter)->context; + r->wayland.vp = wp_viewporter_get_viewport(wp, r->wayland.framebuffer.surface); + wp_viewport_set_source(r->wayland.vp, r->wayland.x, r->wayland.y, r->wayland.ww, r->wayland.wh); + wp_viewport_set_destination(r->wayland.vp, r->wayland.ww - (SSD_BORDER_FRAME_LEFT + SSD_BORDER_FRAME_RIGHT), r->wayland.wh - (SSD_BORDER_FRAME_TOP + SSD_BORDER_FRAME_BOTTOM)); + r->wayland.has_decorations = MwFALSE; + + wl_subsurface_set_position(r->wayland.toplevel->ssurface, SSD_BORDER_FRAME_LEFT, SSD_BORDER_FRAME_TOP); + wl_subsurface_set_desync(r->wayland.toplevel->ssurface); } } @@ -1269,6 +1321,7 @@ static void setup_sublevel(MwLL parent, MwLL r, int x, int y) { /* Sublevel setup function */ static void destroy_sublevel(MwLL r) { + backbuffer_destroy(&r->wayland); framebuffer_destroy(&r->wayland); wl_subsurface_destroy(r->wayland.sublevel->subsurface); @@ -1365,6 +1418,7 @@ static void setup_popup(MwLL r, int x, int y) { // framebuffer_destroy(&r->wayland); framebuffer_setup(&r->wayland); + backbuffer_setup(&r->wayland); } /* Popup destroy function */ @@ -1424,6 +1478,7 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) { } framebuffer_setup(&r->wayland); + backbuffer_setup(&r->wayland); r->wayland.region = wl_compositor_create_region(r->wayland.compositor); r->wayland.o_region = wl_compositor_create_region(r->wayland.compositor); @@ -1515,8 +1570,6 @@ static void MwLLSetXYImpl(MwLL handle, int x, int y) { if(handle->wayland.type == MWLL_WAYLAND_SUBLEVEL) { wl_subsurface_set_position(handle->wayland.sublevel->subsurface, x, y); } - if(handle->wayland.type == MWLL_WAYLAND_POPUP) { - } region_setup(handle); MwLLDispatch(handle, draw, NULL); @@ -1531,17 +1584,29 @@ static void MwLLSetWHImpl(MwLL handle, int w, int h) { handle->wayland.wh = 10; goto refresh; } + if(handle->wayland.parent) { + if(handle->wayland.parent->wayland.type == MWLL_WAYLAND_TOPLEVEL && !handle->wayland.parent->wayland.has_decorations) { + if(w >= handle->wayland.parent->wayland.ww - (SSD_BORDER_FRAME_LEFT + SSD_BORDER_FRAME_RIGHT)) + w = handle->wayland.parent->wayland.ww - (SSD_BORDER_FRAME_LEFT + SSD_BORDER_FRAME_RIGHT); + if(h >= handle->wayland.parent->wayland.wh - (SSD_BORDER_FRAME_TOP + SSD_BORDER_FRAME_BOTTOM)) + h = handle->wayland.parent->wayland.wh - (SSD_BORDER_FRAME_TOP + SSD_BORDER_FRAME_BOTTOM); + } + } handle->wayland.ww = w; handle->wayland.wh = h; + // if(handle->wayland.wh >= (SSD_BORDER_FRAME_TOP + SSD_BORDER_FRAME_BOTTOM)) { + // handle->wayland.wh -= (SSD_BORDER_FRAME_TOP + SSD_BORDER_FRAME_BOTTOM); + // } + if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL && handle->wayland.configured) { xdg_surface_set_window_geometry(handle->wayland.toplevel->xdg_surface, 0, 0, handle->wayland.ww, handle->wayland.wh); } if(handle->wayland.type == MWLL_WAYLAND_POPUP) { - // destroy_popup(handle); - // wl_flush(handle); - // setup_popup(handle, handle->wayland.x, handle->wayland.y); + destroy_popup(handle); + wl_flush(handle); + setup_popup(handle, handle->wayland.x, handle->wayland.y); } refresh: @@ -1549,31 +1614,48 @@ refresh: framebuffer_destroy(&handle->wayland); framebuffer_setup(&handle->wayland); + backbuffer_destroy(&handle->wayland); + backbuffer_setup(&handle->wayland); MwLLDispatch(handle, draw, NULL); } static void MwLLBeginDrawImpl(MwLL handle) { - (void)handle; + if(handle->wayland.type != MWLL_WAYLAND_TOPLEVEL) { + return; + } + if(!handle->wayland.has_decorations) { + cairo_set_source_rgb(handle->wayland.back_cairo, 0, 0.25, 0.25); + cairo_rectangle(handle->wayland.back_cairo, 1, 1, handle->wayland.ww - 2, handle->wayland.wh - 2); + cairo_fill_preserve(handle->wayland.back_cairo); + cairo_set_source_rgb(handle->wayland.back_cairo, 1, 1, 1); + cairo_rectangle(handle->wayland.back_cairo, 0, 0, handle->wayland.ww, handle->wayland.wh); + cairo_stroke(handle->wayland.back_cairo); + } } static void MwLLEndDrawImpl(MwLL handle) { - if(handle->wayland.configured) update_buffer(&handle->wayland.framebuffer); + if(handle->wayland.configured) { + update_buffer(&handle->wayland.framebuffer); + if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) { + update_buffer(&handle->wayland.backbuffer); + } + } } static void MwLLPolygonImpl(MwLL handle, MwPoint* points, int points_count, MwLLColor color) { int i; - cairo_set_source_rgb(handle->wayland.cairo, color->common.red / 255.0, color->common.green / 255.0, color->common.blue / 255.0); - cairo_new_path(handle->wayland.cairo); + cairo_set_source_rgb(handle->wayland.front_cairo, color->common.red / 255.0, color->common.green / 255.0, color->common.blue / 255.0); + cairo_new_path(handle->wayland.front_cairo); for(i = 0; i < points_count; i++) { if(i == 0) { - cairo_move_to(handle->wayland.cairo, points[i].x, points[i].y); + cairo_move_to(handle->wayland.front_cairo, points[i].x, points[i].y); } else { - cairo_line_to(handle->wayland.cairo, points[i].x, points[i].y); + cairo_line_to(handle->wayland.front_cairo, points[i].x, points[i].y); } } - cairo_close_path(handle->wayland.cairo); + cairo_close_path(handle->wayland.front_cairo); - cairo_fill(handle->wayland.cairo); + cairo_fill(handle->wayland.front_cairo); handle->wayland.events_pending = 1; } @@ -1581,18 +1663,18 @@ static void MwLLPolygonImpl(MwLL handle, MwPoint* points, int points_count, MwLL static void MwLLLineImpl(MwLL handle, MwPoint* points, MwLLColor color) { int i; - cairo_set_line_cap(handle->wayland.cairo, CAIRO_LINE_CAP_SQUARE); - cairo_set_source_rgb(handle->wayland.cairo, color->common.red / 255.0, color->common.green / 255.0, color->common.blue / 255.0); - cairo_new_path(handle->wayland.cairo); + cairo_set_line_cap(handle->wayland.front_cairo, CAIRO_LINE_CAP_SQUARE); + cairo_set_source_rgb(handle->wayland.front_cairo, color->common.red / 255.0, color->common.green / 255.0, color->common.blue / 255.0); + cairo_new_path(handle->wayland.front_cairo); for(i = 0; i < 2; i++) { if(i == 0) { - cairo_move_to(handle->wayland.cairo, points[i].x, points[i].y); + cairo_move_to(handle->wayland.front_cairo, points[i].x, points[i].y); } else { - cairo_line_to(handle->wayland.cairo, points[i].x, points[i].y); + cairo_line_to(handle->wayland.front_cairo, points[i].x, points[i].y); } } - cairo_close_path(handle->wayland.cairo); - cairo_stroke(handle->wayland.cairo); + cairo_close_path(handle->wayland.front_cairo); + cairo_stroke(handle->wayland.front_cairo); handle->wayland.events_pending = 1; } @@ -1638,6 +1720,9 @@ static int MwLLPendingImpl(MwLL handle) { } } wl_surface_commit(handle->wayland.framebuffer.surface); + if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) { + wl_surface_commit(handle->wayland.backbuffer.surface); + } if(handle->wayland.always_render) { event_loop(handle); @@ -1675,6 +1760,9 @@ static void MwLLSetTitleImpl(MwLL handle, const char* title) { if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) { xdg_toplevel_set_title(handle->wayland.toplevel->xdg_top_level, title); } + if(!handle->wayland.has_decorations) { + strncpy(handle->wayland.title, title, 255); + } } static MwLLPixmap MwLLCreatePixmapImpl(MwLL handle, unsigned char* data, int width, int height) { @@ -1726,11 +1814,6 @@ static void MwLLDrawPixmapImpl(MwLL handle, MwRect* rect, MwLLPixmap pixmap) { cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, rect->width, rect->height); c = cairo_create(cs); - if(parent) { - cairo_rectangle(c, 0, 0, parent->wayland.ww + handle->wayland.x, parent->wayland.wh + handle->wayland.y); - cairo_clip(c); - } - cairo_scale(c, (double)rect->width / pixmap->common.width, (double)rect->height / pixmap->common.height); cairo_set_source_surface(c, pixmap->wayland.cs, 0, 0); @@ -1738,8 +1821,8 @@ static void MwLLDrawPixmapImpl(MwLL handle, MwRect* rect, MwLLPixmap pixmap) { cairo_paint(c); - cairo_set_source_surface(handle->wayland.cairo, cs, rect->x, rect->y); - cairo_paint(handle->wayland.cairo); + cairo_set_source_surface(handle->wayland.front_cairo, cs, rect->x, rect->y); + cairo_paint(handle->wayland.front_cairo); cairo_destroy(c); cairo_surface_destroy(cs);