#define WRASTER_NO_XLIB /* Wayland-only file: no libX11 deps */ /* * wview_wl.c - Wayland view backend for WINGs * * Views are logical layout containers in Wayland mode. * No X11 dependencies. Rendering is handled by frame_paint (pixman buffers). */ #include "WINGsP.h" #include /* Synthetic window IDs for Wayland views (distinct from frame buffer IDs) */ #define WL_VIEW_ID_BASE 0x20000000 static unsigned int wl_view_next_id = WL_VIEW_ID_BASE; /* Callback set by WM backend to commit frame_buf after paint */ void (*W_ViewCommitBackingFn)(unsigned long id) = NULL; void (*W_ViewDestroyBackingFn)(unsigned long id) = NULL; /* Callback for wclient_wl.c to trigger a full redisplay after resize */ static void wl_redisplay(void *vctx, W_View *view); static void wl_client_redisplay_wrapper(W_View *view) { if (view && view->screen) wl_redisplay(view->screen->viewBackendCtx, view); } void (*W_WLClientRedisplayFn)(W_View *view) = wl_client_redisplay_wrapper; /* Clipboard hooks set by WM backend for cross-client clipboard */ void (*W_ClipboardStoreFn)(const char *text, int len) = NULL; char *(*W_ClipboardFetchFn)(int *len_out) = NULL; /* Drag icon overlay hooks set by WM backend. * Create/show a scene-graph node for the drag image, move it with the * cursor, and hide/destroy it when the drag ends. * x,y are in output (root-view) coordinates. */ void (*W_DragIconShowFn)(W_View *view, int x, int y) = NULL; void (*W_DragIconMoveFn)(int x, int y) = NULL; void (*W_DragIconHideFn)(void) = NULL; /* Drag cursor feedback — called during drag loop when the view under the * cursor changes. accepted=True means the view accepts the drop. */ void (*W_DragCursorFeedbackFn)(Bool accepted) = NULL; extern void wl_view_destroy_backing_impl(unsigned long id); /* Simple view lookup table (ID → W_View*) */ #define VIEW_TABLE_SIZE 256 static struct { WNativeWindow id; W_View *view; void *backing; } view_table[VIEW_TABLE_SIZE]; static void view_table_put(WNativeWindow id, W_View *view) { unsigned idx = ((unsigned)id) % VIEW_TABLE_SIZE; for (int i = 0; i < VIEW_TABLE_SIZE; i++) { unsigned slot = (idx + i) % VIEW_TABLE_SIZE; if (view_table[slot].id == 0 || view_table[slot].id == id) { view_table[slot].id = id; view_table[slot].view = view; return; } } } W_View *W_GetViewForWindow(WNativeWindow id) { unsigned idx = ((unsigned)id) % VIEW_TABLE_SIZE; for (int i = 0; i < VIEW_TABLE_SIZE; i++) { unsigned slot = (idx + i) % VIEW_TABLE_SIZE; if (view_table[slot].id == id) return view_table[slot].view; if (view_table[slot].id == 0) { //wwarning("W_GetViewForWindow: id=0x%lx NOT FOUND (slot %u empty)", (unsigned long)id, slot); return NULL; } } return NULL; } /* Get/set backing pixman image for a view ID */ void *W_GetViewBacking(WNativeWindow id) { unsigned idx = ((unsigned)id) % VIEW_TABLE_SIZE; for (int i = 0; i < VIEW_TABLE_SIZE; i++) { unsigned slot = (idx + i) % VIEW_TABLE_SIZE; if (view_table[slot].id == id) return view_table[slot].backing; if (view_table[slot].id == 0) return NULL; } return NULL; } void W_SetViewBacking(WNativeWindow id, void *img) { unsigned idx = ((unsigned)id) % VIEW_TABLE_SIZE; for (int i = 0; i < VIEW_TABLE_SIZE; i++) { unsigned slot = (idx + i) % VIEW_TABLE_SIZE; if (view_table[slot].id == id) { view_table[slot].backing = img; return; } if (view_table[slot].id == 0) return; } } static void view_table_remove(WNativeWindow id) { unsigned idx = ((unsigned)id) % VIEW_TABLE_SIZE; for (int i = 0; i < VIEW_TABLE_SIZE; i++) { unsigned slot = (idx + i) % VIEW_TABLE_SIZE; if (view_table[slot].id == id) { view_table[slot].id = 0; view_table[slot].view = NULL; view_table[slot].backing = NULL; return; } if (view_table[slot].id == 0) return; } } Bool W_IsValidView(W_View *view) { if (!view) return False; unsigned idx = ((unsigned)view->window) % VIEW_TABLE_SIZE; for (int i = 0; i < VIEW_TABLE_SIZE; i++) { unsigned slot = (idx + i) % VIEW_TABLE_SIZE; if (view_table[slot].view == view) return True; if (view_table[slot].id == 0) return False; } return False; } /* * W_RegisterBacking - register a pixman_image_t backing for any synthetic * window ID (WINGs views OR WM frame_buf windows). Used by wl_backend.c so * that WMDrawString() can resolve a WM window ID to a pixman_image_t when * drawing text into frame_buf images (e.g. miniwindow title). */ void W_RegisterBacking(WNativeWindow id, void *img) { view_table_put(id, NULL); W_SetViewBacking(id, img); } /* W_UnregisterBacking - remove a backing registration (on frame_buf destroy). */ void W_UnregisterBacking(WNativeWindow id) { view_table_remove(id); } static void *wl_screen_init(W_Screen *scr) { (void)scr; return scr; /* use scr as token */ } static void wl_screen_fini(void *vctx) { (void)vctx; } static void wl_create_root(void *vctx, W_View *view) { (void)vctx; view->window = (WNativeWindow)wl_view_next_id++; view_table_put(view->window, view); /* Default screen size — the WM backend will update this */ view->size.width = 1920; view->size.height = 1080; } /* Optional hook set by wclient_wl.c (Wayland-client mode) so that * top-level views allocate their backing as wl_shm-mapped pixman images * paired with a wl_surface + xdg_toplevel. When NULL (compositor mode * or X11), wl_realize falls back to a free-floating pixman image. */ void *(*W_WLClientToplevelRealizeFn)(W_View *view, int width, int height) = NULL; void (*W_WLClientToplevelResizeFn)(W_View *view, int width, int height) = NULL; void (*W_WLClientToplevelDestroyFn)(W_View *view) = NULL; static void wl_realize(void *vctx, W_View *view) { (void)vctx; /* Assign a synthetic ID so W_DispatchMessage doesn't skip this view */ view->window = (WNativeWindow)wl_view_next_id++; view_table_put(view->window, view); /* Allocate a backing pixman image for this view */ int w = view->size.width > 0 ? view->size.width : 1; int h = view->size.height > 0 ? view->size.height : 1; pixman_image_t *img = NULL; if (view->flags.topLevel && W_WLClientToplevelRealizeFn) img = (pixman_image_t *)W_WLClientToplevelRealizeFn(view, w, h); if (!img) img = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h, NULL, 0); W_SetViewBacking(view->window, img); } static void wl_redisplay(void *vctx, W_View *view); static void wl_unmap(void *vctx, W_View *view) { (void)vctx; view->flags.mapped = 0; if (view->parent && view->parent->flags.mapped) wl_redisplay(vctx, view->parent); } static void wl_unmap_subviews(void *vctx, W_View *view) { W_View *child = view->childrenList; while (child) { child->flags.mapped = 0; if (child->childrenList) wl_unmap_subviews(vctx, child); child = child->nextSister; } } static void wl_raise(void *vctx, W_View *view) { (void)vctx; W_View *parent = view->parent; if (!parent) return; /* Remove from list */ if (parent->childrenList == view) { parent->childrenList = view->nextSister; } else { W_View *prev = parent->childrenList; while (prev && prev->nextSister != view) prev = prev->nextSister; if (prev) prev->nextSister = view->nextSister; } /* Append to end (drawn last = on top) */ view->nextSister = NULL; if (!parent->childrenList) { parent->childrenList = view; } else { W_View *last = parent->childrenList; while (last->nextSister) last = last->nextSister; last->nextSister = view; } if (parent->flags.mapped) wl_redisplay(vctx, parent); } static void wl_lower(void *vctx, W_View *view) { (void)vctx; W_View *parent = view->parent; if (!parent) return; /* Remove from list */ if (parent->childrenList == view) { parent->childrenList = view->nextSister; } else { W_View *prev = parent->childrenList; while (prev && prev->nextSister != view) prev = prev->nextSister; if (prev) prev->nextSister = view->nextSister; } /* Prepend to front (drawn first = on bottom) */ view->nextSister = parent->childrenList; parent->childrenList = view; if (parent->flags.mapped) wl_redisplay(vctx, parent); } static void wl_map_subviews(void *vctx, W_View *view); static int wl_redisplay_depth = 0; int W_WL_Realizing = 0; static void wl_map(void *vctx, W_View *view) { (void)vctx; view->flags.mapped = 1; if (!view->flags.realized) return; if (view->flags.topLevel) { wl_redisplay(vctx, view); return; } /* Non-toplevel: find the toplevel and redisplay from there, * but only if all ancestors are realized (not mid-realization). */ W_View *top = view->parent; while (top && !top->flags.topLevel) { if (!top->flags.realized) return; top = top->parent; } if (top && top->flags.realized && top->flags.mapped && wl_redisplay_depth == 0 && !W_WL_Realizing) { wl_redisplay(vctx, top); } } static void wl_map_subviews(void *vctx, W_View *view) { W_View *child = view->childrenList; while (child) { if (child->flags.realized) { child->flags.mapped = 1; } if (child->childrenList) wl_map_subviews(vctx, child); child = child->nextSister; } /* Redisplay the parent which will recurse into all now-mapped children */ if (view->flags.mapped) wl_redisplay(vctx, view); } static void wl_move(void *vctx, W_View *view, int x, int y) { (void)vctx; view->pos.x = x; view->pos.y = y; /* For popup-style toplevels, drive position via wmaker-popup-v1. * The client function is a no-op if the toplevel hasn't been * realized yet — pos.x/y is captured into view->pos for the * realize-time call. */ if (view->flags.topLevel && view->flags.popup && view->flags.realized) { extern void W_WLClientPopupSetPosition(WNativeWindow w, int x, int y); W_WLClientPopupSetPosition((WNativeWindow)view->window, x, y); } if (view->parent && view->flags.mapped) wl_redisplay(vctx, view->parent); } static void wl_resize(void *vctx, W_View *view, unsigned int w, unsigned int h) { (void)vctx; view->size.width = w; view->size.height = h; int bw = w > 0 ? (int)w : 1; int bh = h > 0 ? (int)h : 1; if (view->flags.topLevel && W_WLClientToplevelResizeFn) { /* Wayland-client mode: backend swaps to a freshly allocated * shm-backed pixman image and updates the surface. */ W_WLClientToplevelResizeFn(view, bw, bh); } else { /* Default path: free old image, allocate a new one. */ pixman_image_t *old = (pixman_image_t *)W_GetViewBacking(view->window); if (old) pixman_image_unref(old); pixman_image_t *img = pixman_image_create_bits(PIXMAN_a8r8g8b8, bw, bh, NULL, 0); W_SetViewBacking(view->window, img); } if (view->flags.mapped) { /* Notify parent of child resize (mirrors X11 ConfigureNotify * which scroll views use to update scroller proportions). */ if (view->parent) { WMEvent cev; memset(&cev, 0, sizeof(cev)); cev.type = WME_CONFIGURE; cev.window = view->window; cev.u.configure.window = view->window; cev.u.configure.event = view->parent->window; cev.u.configure.width = w; cev.u.configure.height = h; cev._view_target = view->parent; WMHandleEvent(&cev); } /* Only redisplay if not already inside a redisplay (prevents * infinite recursion when expose handlers trigger resizes). */ if (wl_redisplay_depth == 0) wl_redisplay(vctx, view); } } static void wl_reparent(void *vctx, W_View *view, W_View *newParent, int x, int y) { (void)vctx; W_View *oldParent = view->parent; /* Remove from old parent's children list */ if (oldParent) { if (oldParent->childrenList == view) { oldParent->childrenList = view->nextSister; } else { W_View *prev = oldParent->childrenList; while (prev && prev->nextSister != view) prev = prev->nextSister; if (prev) prev->nextSister = view->nextSister; } } /* Insert into new parent's children list */ view->nextSister = newParent->childrenList; newParent->childrenList = view; view->parent = newParent; view->pos.x = x; view->pos.y = y; if (oldParent && oldParent->flags.mapped) wl_redisplay(vctx, oldParent); if (view->flags.mapped) wl_redisplay(vctx, newParent); } static void wl_destroy(void *vctx, W_View *view) { (void)vctx; W_WL_InvalidateView(view->screen, view); if (view->flags.topLevel && W_WLClientToplevelDestroyFn) W_WLClientToplevelDestroyFn(view); if (view->flags.topLevel && W_ViewDestroyBackingFn) W_ViewDestroyBackingFn((unsigned long)view->window); } static void wl_set_background_color(void *vctx, W_View *view, WMColor *color) { (void)vctx; (void)view; (void)color; /* Background is handled by pixman rendering in frame_paint */ } static void wl_set_cursor(void *vctx, W_View *view, WMNativeCursor cursor) { (void)vctx; (void)view; (void)cursor; /* WMNativeCursor is managed by wlroots seat */ } static void wl_redisplay(void *vctx, W_View *view) { WMEvent ev; (void)vctx; if (!view->flags.mapped || view->flags.alreadyDead) return; wl_redisplay_depth++; /* Fill background color */ pixman_image_t *backing = (pixman_image_t *)W_GetViewBacking(view->window); if (backing) { int bw = pixman_image_get_width(backing); int bh = pixman_image_get_height(backing); pixman_color_t bg; if (view->backColor) { bg.red = view->backColor->color.red; bg.green = view->backColor->color.green; bg.blue = view->backColor->color.blue; bg.alpha = view->backColor->alpha ? view->backColor->alpha : 0xFFFF; } else { bg = (pixman_color_t){0xAAAA, 0xAAAA, 0xAAAA, 0xFFFF}; } pixman_image_fill_rectangles(PIXMAN_OP_SRC, backing, &bg, 1, &(pixman_rectangle16_t){0, 0, bw, bh}); } memset(&ev, 0, sizeof(ev)); ev.type = WME_EXPOSE; ev.window = (WNativeWindow)view->window; ev.u.expose.window = (WNativeWindow)view->window; ev.u.expose.count = 0; ev._x11_raw = NULL; ev._view_target = view; WMHandleEvent(&ev); /* Re-composite all mapped children into this view's backing */ W_View *child = view->childrenList; while (child) { W_View *next = child->nextSister; if (child->flags.mapped && child->flags.realized && !child->flags.alreadyDead) { wl_redisplay(vctx, child); } child = next; } /* Composite this view's backing into its parent's backing */ if (view->parent && view->size.width > 0 && view->size.height > 0) { pixman_image_t *src = (pixman_image_t *)W_GetViewBacking(view->window); pixman_image_t *dst = (pixman_image_t *)W_GetViewBacking(view->parent->window); if (src && dst) { int sw = pixman_image_get_width(src); int sh = pixman_image_get_height(src); int dw = pixman_image_get_width(dst); int dh = pixman_image_get_height(dst); int cw = sw < (int)view->size.width ? sw : (int)view->size.width; int ch = sh < (int)view->size.height ? sh : (int)view->size.height; if (view->pos.x + cw > dw) cw = dw - view->pos.x; if (view->pos.y + ch > dh) ch = dh - view->pos.y; if (cw > 0 && ch > 0 && view->pos.x >= 0 && view->pos.y >= 0) pixman_image_composite32(PIXMAN_OP_OVER, src, NULL, dst, 0, 0, 0, 0, view->pos.x, view->pos.y, cw, ch); } } /* Notify WM backend to commit the top-level view's backing buffer */ /* Walk up to find the top-level view */ W_View *top = view; while (top->parent && !top->flags.topLevel) top = top->parent; extern void (*W_ViewCommitBackingFn)(unsigned long id); if (W_ViewCommitBackingFn) W_ViewCommitBackingFn((unsigned long)top->window); wl_redisplay_depth--; } /* Find the deepest child view at (x, y) relative to the given view. */ W_View *W_FindViewAtPoint(W_View *parent, int x, int y) { if (!parent) return NULL; W_View *child = parent->childrenList; while (child) { if (child->flags.mapped && child->flags.realized && x >= child->pos.x && x < child->pos.x + child->size.width && y >= child->pos.y && y < child->pos.y + child->size.height) { /* Recurse into this child with local coordinates */ W_View *deeper = W_FindViewAtPoint(child, x - child->pos.x, y - child->pos.y); return deeper ? deeper : child; } child = child->nextSister; } return NULL; } static WMPoint wl_get_screen_position(void *vctx, W_View *view) { W_Screen *scr = (W_Screen *)vctx; int x = 0, y = 0; W_View *v; /* Walk up the view tree accumulating positions */ for (v = view; v && v != scr->rootView; v = v->parent) { x += v->pos.x; y += v->pos.y; } return wmkpoint(x, y); } static Bool wl_selection_acquire(void *vctx, W_View *view, WMNativeAtom selection, Time timestamp) { (void)vctx; (void)view; (void)selection; (void)timestamp; /* TODO: implement via wl_data_device */ return True; } static void wl_selection_release(void *vctx, W_View *view, WMNativeAtom selection) { (void)vctx; (void)view; (void)selection; } static char *wl_selection_fetch(void *vctx, WMNativeAtom selection, int *len_out) { (void)vctx; (void)selection; /* Use the clipboard fetch path (reads from seat selection via pipe) */ if (W_ClipboardFetchFn) return W_ClipboardFetchFn(len_out); if (len_out) *len_out = 0; return NULL; } static char *wl_clipboard_buf = NULL; static int wl_clipboard_len = 0; static void wl_clipboard_store(void *vctx, const char *text, int len) { (void)vctx; if (wl_clipboard_buf) wfree(wl_clipboard_buf); if (text && len > 0) { wl_clipboard_buf = wmalloc(len + 1); memcpy(wl_clipboard_buf, text, len); wl_clipboard_buf[len] = '\0'; wl_clipboard_len = len; if (W_ClipboardStoreFn) W_ClipboardStoreFn(text, len); } else { wl_clipboard_buf = NULL; wl_clipboard_len = 0; } } static char *wl_clipboard_fetch(void *vctx, int *len_out) { (void)vctx; /* Try seat selection first (cross-client paste) */ if (W_ClipboardFetchFn) { int n = 0; char *text = W_ClipboardFetchFn(&n); if (text) { if (len_out) *len_out = n; return text; } } /* Fall back to internal buffer */ if (wl_clipboard_buf && wl_clipboard_len > 0) { char *copy = wmalloc(wl_clipboard_len + 1); memcpy(copy, wl_clipboard_buf, wl_clipboard_len); copy[wl_clipboard_len] = '\0'; if (len_out) *len_out = wl_clipboard_len; return copy; } if (len_out) *len_out = 0; return NULL; } static void wl_text_init(void *vctx, W_View *view, void *stipple_gc_out) { (void)vctx; (void)view; (void)stipple_gc_out; } static void wl_balloon_draw(void *vctx, W_View *view, int x, int y, int w, int h, const char *text) { (void)vctx; if (!view || !text || !text[0]) return; W_Screen *scr = view->screen; WMFont *font = scr->normalFont; int fh = WMFontHeight(font); /* Count lines and find widest line for multi-line text */ int maxWidth = 0, numLines = 1; const char *p = text; while (*p) { const char *nl = strchr(p, '\n'); int lineLen = nl ? (int)(nl - p) : (int)strlen(p); int lw = WMWidthOfString(font, p, lineLen); if (lw > maxWidth) maxWidth = lw; p = nl ? nl + 1 : p + lineLen; if (nl) numLines++; } int bw = maxWidth + 16; int bh = numLines * fh + 8; if (bw < bh) bw = bh; int bx = x + w / 2 - bw / 2; int by = y - bh - 4; if (by < 0) by = y + h + 4; if (bx < 0) bx = 0; W_ResizeView(view, bw, bh); W_MoveView(view, bx, by); if (!view->flags.mapped) W_MapView(view); pixman_image_t *img = (pixman_image_t *)W_GetViewBacking(view->window); if (!img) return; pixman_color_t yellow = {0xFFFF, 0xFFFF, 0x8888, 0xFFFF}; pixman_image_fill_rectangles(PIXMAN_OP_SRC, img, &yellow, 1, &(pixman_rectangle16_t){0, 0, bw, bh}); pixman_color_t black = {0, 0, 0, 0xFFFF}; pixman_image_fill_rectangles(PIXMAN_OP_SRC, img, &black, 4, (pixman_rectangle16_t[]){ {0, 0, bw, 1}, {0, bh - 1, bw, 1}, {0, 0, 1, bh}, {bw - 1, 0, 1, bh} }); /* Draw each line */ p = text; int line = 0; while (*p) { const char *nl = strchr(p, '\n'); int lineLen = nl ? (int)(nl - p) : (int)strlen(p); int ty = 4 + line * fh; WMDrawString(scr, (WMDrawable)view->window, scr->black, font, 8, ty, p, lineLen); p = nl ? nl + 1 : p + lineLen; line++; } } static void wl_drag_begin(void *vctx, W_View *view, WMEvent *event) { W_Screen *scr = (W_Screen *)vctx; if (!view->dragSourceProcs || event->type != WME_BUTTON_PRESS) return; /* Wait for motion to confirm drag */ WMEvent ev; WMMaskEvent(scr, WM_EVMASK_BUTTON_MOTION | WM_EVMASK_BUTTON_RELEASE, &ev); if (ev.type != WME_MOTION) return; if (view->dragSourceProcs->beganDrag) { WMPoint pt = wmkpoint(ev.u.motion.x_root, ev.u.motion.y_root); view->dragSourceProcs->beganDrag(view, &pt); } /* Show drag icon overlay at cursor position */ int mx = ev.u.motion.x_root; int my = ev.u.motion.y_root; if (W_DragIconShowFn) W_DragIconShowFn(view, mx, my); /* Drag loop */ W_View *destView = NULL; Bool deposited = False; int done = 0; while (!done) { WMMaskEvent(scr, WM_EVMASK_BUTTON_MOTION | WM_EVMASK_BUTTON_RELEASE, &ev); switch (ev.type) { case WME_MOTION: mx = ev.u.motion.x_root; my = ev.u.motion.y_root; /* Move drag icon to follow cursor */ if (W_DragIconMoveFn) W_DragIconMoveFn(mx, my); /* Find view under cursor */ { W_View *top = scr->rootView; destView = W_FindViewAtPoint(top, mx, my); /* Cursor feedback: does the view accept drops? */ if (W_DragCursorFeedbackFn) { Bool accepted = (destView && destView->dragDestinationProcs && destView->droppableTypes); W_DragCursorFeedbackFn(accepted); } } break; case WME_BUTTON_RELEASE: if (destView && destView->dragDestinationProcs && destView->droppableTypes && view->dragSourceProcs->dropDataTypes) { WMArray *types = view->dragSourceProcs->dropDataTypes(view); WMArray *dropData = WMCreateArray(WMGetArrayItemCount(types)); int i; for (i = 0; i < WMGetArrayItemCount(types); i++) { char *type = WMGetFromArray(types, i); WMData *data = view->dragSourceProcs->fetchDragData(view, type); if (data) WMAddToArray(dropData, data); } if (WMGetArrayItemCount(dropData) > 0 && destView->dragDestinationProcs->performDragOperation) { WMPoint pt = wmkpoint(ev.u.button.x_root, ev.u.button.y_root); destView->dragDestinationProcs->performDragOperation( destView, dropData, NULL, &pt); deposited = True; } WMFreeArray(dropData); } done = 1; break; } } /* Hide/destroy drag icon overlay */ if (W_DragIconHideFn) W_DragIconHideFn(); if (view->dragSourceProcs->endedDrag) { WMPoint pt = wmkpoint(ev.u.button.x_root, ev.u.button.y_root); view->dragSourceProcs->endedDrag(view, &pt, deposited); } } static void wl_flush(void *vctx) { (void)vctx; } static void wl_window_set_title(void *vctx, W_View *view, const char *title) { (void)vctx; if (!view || !title) return; extern void W_WLClientSetTitle(WNativeWindow window, const char *title); W_WLClientSetTitle(view->window, title); } static void wl_window_set_icon_title(void *vctx, W_View *view, const char *title) { (void)vctx; (void)view; (void)title; } static void wl_window_set_icon_image(void *vctx, W_View *view, RImage *image) { (void)vctx; (void)view; (void)image; } static void wl_window_realize(void *vctx, W_View *view, const char *name, const char *class, W_View *owner) { (void)vctx; (void)view; (void)name; (void)class; (void)owner; } static void wl_pointer_grab(void *vctx, W_View *view, int owner_events, unsigned long time) { (void)vctx; (void)view; (void)owner_events; (void)time; /* No-op: Wayland uses implicit grab on button press; popup menus * receive events via the pointer focus mechanism. */ } static void wl_pointer_ungrab(void *vctx, unsigned long time) { (void)vctx; (void)time; } static void wl_window_set_geometry(void *vctx, W_View *view) { (void)vctx; if (!view || !view->flags.topLevel) return; extern void W_WLClientSetMinMaxSize(W_View *view); W_WLClientSetMinMaxSize(view); } static void wl_window_withdraw(void *vctx, W_View *view) { (void)vctx; (void)view; } const W_ViewBackend W_ViewBackendWL = { .screen_init = wl_screen_init, .screen_fini = wl_screen_fini, .create_root = wl_create_root, .realize = wl_realize, .map = wl_map, .unmap = wl_unmap, .map_subviews = wl_map_subviews, .unmap_subviews = wl_unmap_subviews, .move = wl_move, .resize = wl_resize, .raise = wl_raise, .lower = wl_lower, .reparent = wl_reparent, .destroy = wl_destroy, .set_background_color = wl_set_background_color, .set_cursor = wl_set_cursor, .redisplay = wl_redisplay, .get_screen_position = wl_get_screen_position, .selection_acquire = wl_selection_acquire, .selection_release = wl_selection_release, .selection_request = NULL, .selection_fetch = wl_selection_fetch, .platform_init = NULL, .flush = wl_flush, .clipboard_store = wl_clipboard_store, .clipboard_fetch = wl_clipboard_fetch, .drag_begin = wl_drag_begin, .window_set_title = wl_window_set_title, .window_set_icon_title = wl_window_set_icon_title, .window_set_icon_image = wl_window_set_icon_image, .window_realize = wl_window_realize, .window_set_geometry = wl_window_set_geometry, .window_withdraw = wl_window_withdraw, .pointer_grab = wl_pointer_grab, .pointer_ungrab = wl_pointer_ungrab, .bell = NULL, .balloon_draw = wl_balloon_draw, .text_init = wl_text_init, .xor_fill_rect = NULL, .magnify_callback = NULL, };