diff --git a/src/Makefile.am b/src/Makefile.am index 01fa47b..13a7d75 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -163,7 +163,8 @@ wmaker_SOURCES += \ backend/wayland/wl_stacking.c \ backend/wayland/wl_overlay.c \ backend/wayland/wl_window.c \ - backend/wayland/wl_props.c + backend/wayland/wl_props.c \ + backend/wayland/wl_client.c endif if USE_NESTED_FUNC diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 4430ac8..ed73fb4 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -321,7 +321,7 @@ static int wl_catch_xwayland_error(Display *edpy, XErrorEvent *error); /* Phase 43: forward declaration — defined in the focus section below. */ static void wl_focus_set(WNativeWindow win, int revert_to, unsigned long timestamp); /* Phase 50: forward declaration — defined in client section below. */ -static int wl_client_get_pid(WNativeWindow win); +int wl_client_get_pid(WNativeWindow win); /* Phase 13: forward declarations — defined in pointer section below. */ static void wl_update_pointer_focus(uint32_t time_msec); static void wl_pointer_warp_to(int x, int y); @@ -4002,7 +4002,7 @@ static void frame_buf_end_data_ptr_access(struct wlr_buffer *wlr_buf) (void)wlr_buf; } -static const struct wlr_buffer_impl frame_buf_impl = { +const struct wlr_buffer_impl frame_buf_impl = { .destroy = frame_buf_destroy, .begin_data_ptr_access = frame_buf_begin_data_ptr_access, .end_data_ptr_access = frame_buf_end_data_ptr_access, @@ -4044,7 +4044,7 @@ static void fb_tree_destroy_handler(struct wl_listener *l, void *d) * will fire too and NULL fb->scene_buf separately. */ wl_list_remove(&fb->tree_destroy.link); } -static void fb_attach_scene_buf_listener(struct wl_frame_buf *fb) +void fb_attach_scene_buf_listener(struct wl_frame_buf *fb) { if (!fb || !fb->scene_buf) return; fb->scene_buf_destroy.notify = fb_scene_buf_destroy_handler; @@ -6276,473 +6276,6 @@ wl_noop_screen_get_head_usable_area(WScreen *scr, int head, -/* ------------------------------------------------------------------ */ -/* Client */ -/* ------------------------------------------------------------------ */ - -static int -wl_window_get_client_attrs(WNativeWindow win, WClientAttributes *out) -{ - /* Phase 42: return real map_state and geometry from XDG surface. - * Phase 17: XWayland path uses xw_surface->mapped and width/height. */ - struct wl_toplevel_view *v = wl_find_view_by_id(win); - memset(out, 0, sizeof(*out)); - if (v && v->xw_surface) { - if (v->xw_surface->surface) { - out->map_state = WCLIENT_VIEWABLE; - out->width = v->xw_surface->width >= 64 ? v->xw_surface->width : 640; - out->height = v->xw_surface->height >= 64 ? v->xw_surface->height : 480; - } else { - out->map_state = WCLIENT_UNMAPPED; - } - } else if (v && v->xdg_surface && v->xdg_surface->surface) { - out->map_state = WCLIENT_VIEWABLE; - out->width = v->xdg_surface->surface->current.width; - out->height = v->xdg_surface->surface->current.height; - } else { - out->map_state = WCLIENT_UNMAPPED; - } - return 1; -} - -static void -wl_client_get_visual_attrs(WNativeWindow win, WClientVisual *out) -{ - (void)win; - out->depth = 24; out->visual = NULL; out->colormap = 0; -} - -static void -wl_client_adopt_attributes(WNativeWindow win, unsigned long event_mask) -{ (void)win; (void)event_mask; } - -static void -wl_client_reparent(WNativeWindow client, WNativeWindow new_parent, int x, int y) -{ - /* Toplevel view reparented into a frame_buf — record the association - * so wl_window_move can keep them in sync. */ - struct wl_toplevel_view *v = wl_find_view_by_id(client); - if (v) { - v->frame_id = new_parent; - v->frame_rel_x = x; - v->frame_rel_y = y; - /* Position the client surface relative to the frame */ - struct wl_frame_buf *parent_fb = frame_buf_find(new_parent); - if (parent_fb) { - v->x = parent_fb->x + x; - v->y = parent_fb->y + y; - if (v->scene_node) { - /* Move client's scene tree under the frame's tree */ - if (parent_fb->tree) - wlr_scene_node_reparent(&v->scene_node->node, parent_fb->tree); - /* Position at relative offset within the tree */ - wlr_scene_node_set_position(&v->scene_node->node, x, y); - } - } - return; - } - - - - /* For WINGs internal windows: create a child frame_buf so the dialog - * content is visible in the scene graph. */ - if (client >= 0x20000000 && new_parent >= 0x10000000) { - /* client is a WINGs view ID, new_parent is a frame_buf */ - struct wl_frame_buf *parent_fb = frame_buf_find(new_parent); - if (!parent_fb) return; - - /* Check if we already have a frame_buf for this client */ - struct wl_frame_buf *existing = frame_buf_find(client); - if (existing) { - /* Just update parent and position */ - existing->parent_id = new_parent; - existing->rel_x = x; - existing->rel_y = y; - existing->x = parent_fb->x + x; - existing->y = parent_fb->y + y; - if (existing->scene_buf) { - /* Ensure it's under the parent frame's tree, then position - * at relative offset within the tree. */ - if (parent_fb->tree && - existing->scene_buf->node.parent != parent_fb->tree) { - wlr_scene_node_reparent(&existing->scene_buf->node, - parent_fb->tree); - } - if (parent_fb->tree) - wlr_scene_node_set_position(&existing->scene_buf->node, x, y); - else - wlr_scene_node_set_position(&existing->scene_buf->node, - existing->x, existing->y); - } - return; - } - - /* Create a new frame_buf for this WINGs view. - * Get size from the view itself. */ - extern W_View *W_GetViewForWindow(unsigned long id); - W_View *view = W_GetViewForWindow((unsigned long)client); - int w = view ? view->size.width : parent_fb->width; - int h = view ? view->size.height : (parent_fb->height - y); - if (w <= 0) w = 1; - if (h <= 0) h = 1; - - struct wl_frame_buf *fb = wmalloc(sizeof(*fb)); - memset(fb, 0, sizeof(*fb)); - - fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h, NULL, 0); - if (!fb->image) { wfree(fb); return; } - - wlr_buffer_init(&fb->base, &frame_buf_impl, w, h); - - fb->id = client; /* use the WINGs view ID directly */ - fb->parent_id = new_parent; - fb->rel_x = x; - fb->rel_y = y; - fb->x = parent_fb->x + x; - fb->y = parent_fb->y + y; - fb->width = w; - fb->height = h; - - if (wl_state.scene) { - /* Place the WINGs content under the parent frame's tree so it - * moves with the WM-managed dialog decorations. */ - struct wlr_scene_tree *_wp = parent_fb->tree - ? parent_fb->tree - : (wl_state.level_trees[WL_LAYER_FLOATING] - ? wl_state.level_trees[WL_LAYER_FLOATING] - : &wl_state.scene->tree); - fb->scene_buf = wlr_scene_buffer_create(_wp, &fb->base); - if (fb->scene_buf) { - fb_attach_scene_buf_listener(fb); - /* If under the frame tree, use relative coords; otherwise absolute. */ - if (parent_fb->tree) - wlr_scene_node_set_position(&fb->scene_buf->node, x, y); - else - wlr_scene_node_set_position(&fb->scene_buf->node, fb->x, fb->y); - wlr_scene_node_set_enabled(&fb->scene_buf->node, false); - } - } - - wl_list_insert(&wl_state.frame_list, &fb->link); - - /* Connect the WINGs view's backing store to this frame_buf's image */ - extern void W_SetViewBacking(unsigned long id, void *img); - W_SetViewBacking((unsigned long)client, fb->image); - } - /* Reparent an existing frame_buf into a new parent */ - else if (client >= 0x10000000 && new_parent >= 0x10000000) { - struct wl_frame_buf *fb = frame_buf_find(client); - struct wl_frame_buf *parent_fb = frame_buf_find(new_parent); - fprintf(stderr, "reparent_framebuf: client=0x%lx fb=%p new_parent=0x%lx parent_fb=%p\n", - (unsigned long)client, (void*)fb, - (unsigned long)new_parent, (void*)parent_fb); - if (fb) fprintf(stderr, " fb: tree=%p scene_buf=%p pos=(%d,%d)\n", - (void*)fb->tree, (void*)fb->scene_buf, - fb->tree ? fb->tree->node.x : -1, - fb->tree ? fb->tree->node.y : -1); - if (parent_fb) fprintf(stderr, " parent_fb: tree=%p pos=(%d,%d)\n", - (void*)parent_fb->tree, - parent_fb->tree ? parent_fb->tree->node.x : -1, - parent_fb->tree ? parent_fb->tree->node.y : -1); - if (fb && parent_fb) { - fb->parent_id = new_parent; - fb->rel_x = x; - fb->rel_y = y; - fb->x = parent_fb->x + x; - fb->y = parent_fb->y + y; - if (fb->tree) { - /* This frame_buf has a tree (e.g. fake_leader): reparent - * the whole tree so all children move with it. */ - if (parent_fb->tree) - wlr_scene_node_reparent(&fb->tree->node, parent_fb->tree); - wlr_scene_node_set_position(&fb->tree->node, x, y); - } else if (fb->scene_buf) { - /* Plain frame_buf (no tree): reparent scene_buf directly. */ - if (parent_fb->tree) - wlr_scene_node_reparent(&fb->scene_buf->node, parent_fb->tree); - wlr_scene_node_set_position(&fb->scene_buf->node, x, y); - } - /* Update absolute tracking recursively for all descendants */ - frame_buf_update_children_abs(client); - } - } -} - -/* Called by WINGs after a view repaints — commit its frame_buf to scene graph */ -void wl_view_commit_backing_impl(unsigned long id) -{ - struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)id); - WNativeWindow saved_parent_id = 0; - int saved_rel_x = 0, saved_rel_y = 0; - /* If the view was resized, the old frame_buf's image is stale. */ - if (fb) { - extern void *W_GetViewBacking(unsigned long id); - void *current = W_GetViewBacking((unsigned long)id); - if (current != (void *)fb->image) { - /* Save parent relation before destroying — the new frame_buf - * must be positioned within the same parent tree so dialog - * content reparented via wl_client_reparent stays visible. */ - saved_parent_id = fb->parent_id; - saved_rel_x = fb->rel_x; - saved_rel_y = fb->rel_y; - wlr_scene_node_destroy(&fb->scene_buf->node); - wl_list_remove(&fb->link); - wlr_buffer_drop(&fb->base); - wfree(fb); - fb = NULL; - } - } - if (!fb) { - extern W_View *W_GetViewForWindow(unsigned long id); - extern void *W_GetViewBacking(unsigned long id); - W_View *view = W_GetViewForWindow((unsigned long)id); - if (view && view->flags.topLevel) { - fb = wmalloc(sizeof(*fb)); - memset(fb, 0, sizeof(*fb)); - fb->id = id; - fb->parent_id = saved_parent_id; - fb->rel_x = saved_rel_x; - fb->rel_y = saved_rel_y; - fb->x = view->pos.x; - fb->y = view->pos.y; - fb->width = view->size.width; - fb->height = view->size.height; - fb->image = (pixman_image_t *)W_GetViewBacking(id); - if (fb->image) { - pixman_image_ref(fb->image); - wlr_buffer_init(&fb->base, &frame_buf_impl, - pixman_image_get_width(fb->image), - pixman_image_get_height(fb->image)); - if (wl_state.scene) { - struct wlr_scene_tree *parent = NULL; - /* Re-parent under the saved parent's tree so - * dialogs reparented via wl_client_reparent stay - * correctly positioned within their frame. */ - if (saved_parent_id) { - struct wl_frame_buf *pfb = - frame_buf_find(saved_parent_id); - if (pfb && pfb->tree) - parent = pfb->tree; - } - if (!parent) - parent = wl_state.level_trees[WL_LAYER_FLOATING] - ? wl_state.level_trees[WL_LAYER_FLOATING] - : &wl_state.scene->tree; - fb->scene_buf = wlr_scene_buffer_create(parent, &fb->base); - if (fb->scene_buf) { - fb_attach_scene_buf_listener(fb); - /* Position relative to parent's tree or absolute */ - if (parent != &wl_state.scene->tree && saved_parent_id) - wlr_scene_node_set_position(&fb->scene_buf->node, - saved_rel_x, saved_rel_y); - else - wlr_scene_node_set_position(&fb->scene_buf->node, - fb->x, fb->y); - wlr_scene_node_set_enabled(&fb->scene_buf->node, false); - } - } - wl_list_insert(&wl_state.frame_list, &fb->link); - } else { - wfree(fb); - fb = NULL; - } - } - } - if (fb && fb->scene_buf) { - /* Sync position from the WINGs view — it may have moved since - * the frame_buf was created (e.g. switch panel is positioned - * after widget creation via WMMoveWidget). - * When reparented (parent_id set), use the relative offset - * instead of the view's absolute position. */ - extern W_View *W_GetViewForWindow(unsigned long id); - W_View *v = W_GetViewForWindow((unsigned long)id); - if (v && !fb->parent_id) { - if (fb->x != v->pos.x || fb->y != v->pos.y) { - fb->x = v->pos.x; - fb->y = v->pos.y; - wlr_scene_node_set_position(&fb->scene_buf->node, - fb->x, fb->y); - } - } - /* Only enable the scene node if the view is still mapped. - * If the view was unmapped (e.g. move ended), wl_unmap disables - * it via W_ViewSceneNodeSetEnabledFn — don't re-enable here. */ - if (!v || v->flags.mapped) { - wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); - wlr_scene_node_set_enabled(&fb->scene_buf->node, true); - } - } -} -void -wl_view_scene_node_set_enabled(unsigned long id, int enabled) -{ - struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)id); - if (fb && fb->scene_buf) - wlr_scene_node_set_enabled(&fb->scene_buf->node, !!enabled); -} - - -void wl_view_destroy_backing_impl(unsigned long id) -{ - struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)id); - if (!fb) return; - wl_list_remove(&fb->link); - /* fb->scene_buf may have been auto-destroyed by wlroots already if a - * parent tree was destroyed earlier (e.g. wl_frame_destroy on the - * surrounding WMaker frame). The destroy listener NULLs scene_buf - * in that case. */ - if (fb->scene_buf) - wlr_scene_node_destroy(&fb->scene_buf->node); - wlr_buffer_drop(&fb->base); -} - -static void -wl_client_synth_configure(WNativeWindow client_win, - int x, int y, int width, int height, - int border_width, WNativeWindow above) -{ - /* Phase 44: send an XDG configure with the new size to the client. - * Phase 13: also record the WM-assigned position in the view so - * pointer hit-testing (view_surface_at) uses current coordinates. - * Phase 17: XWayland uses wlr_xwayland_surface_configure instead. */ - (void)border_width; (void)above; - struct wl_toplevel_view *v = wl_find_view_by_id(client_win); - if (!v) return; - v->x = x; - v->y = y; - /* Update relative offset so frame_configure keeps us in sync. */ - if (v->frame_id) { - struct wl_frame_buf *pfb = frame_buf_find(v->frame_id); - if (pfb) { - v->frame_rel_x = x - pfb->x; - v->frame_rel_y = y - pfb->y; - } - /* Scene node is child of frame tree — use relative coords */ - if (v->scene_node) - wlr_scene_node_set_position(&v->scene_node->node, - v->frame_rel_x, v->frame_rel_y); - } else { - /* No frame — use absolute coords */ - if (v->scene_node) - wlr_scene_node_set_position(&v->scene_node->node, x, y); - } - if (v->xw_surface) { - wlr_xwayland_surface_configure(v->xw_surface, - (int16_t)x, (int16_t)y, - width > 0 ? (uint16_t)width : v->xw_surface->width, - height > 0 ? (uint16_t)height : v->xw_surface->height); - return; - } - if (!v->xdg_surface || !v->xdg_surface->toplevel) return; - if (width > 0 && height > 0) - wlr_xdg_toplevel_set_size(v->xdg_surface->toplevel, (uint32_t)width, (uint32_t)height); - wlr_xdg_surface_schedule_configure(v->xdg_surface); -} - -static void wl_client_add_to_save_set(WNativeWindow win) { (void)win; } -static void wl_client_remove_from_save_set(WNativeWindow win) { (void)win; } - -static void -wl_client_close(WNativeWindow win) -{ - /* Phase 46: ask the surface to close. - * Phase 17: XWayland uses wlr_xwayland_surface_close. */ - struct wl_toplevel_view *v = wl_find_view_by_id(win); - if (!v) return; - if (v->xw_surface) { - wlr_xwayland_surface_close(v->xw_surface); - } else if (v->xdg_surface && v->xdg_surface->toplevel) { - wlr_xdg_toplevel_send_close(v->xdg_surface->toplevel); - /* Flush so the in-process client receives the close event, - * then dispatch so it processes it on this iteration. */ - wl_display_flush_clients(wl_state.display); - } -} - -static void wl_client_query_shaped(WNativeWindow win, int *shaped_out) -{ (void)win; *shaped_out = 0; } - -static int -wl_client_get_gnustep_attrs(WNativeWindow win, GNUstepWMAttributes **attr_out) -{ (void)win; *attr_out = NULL; return 0; } - -static int wl_client_get_pid(WNativeWindow win) -{ - struct wl_toplevel_view *v = wl_find_view_by_id(win); - if (!v) return 0; - /* Phase 17: XWayland surfaces have a direct pid field. */ - if (v->xw_surface) - return (int)v->xw_surface->pid; - if (!v->xdg_surface || !v->xdg_surface->resource) - return 0; - struct wl_client *client = wl_resource_get_client(v->xdg_surface->resource); - if (!client) return 0; - pid_t pid = 0; uid_t uid = 0; gid_t gid = 0; - wl_client_get_credentials(client, &pid, &uid, &gid); - return (int)pid; -} - -static RImage * -wl_client_snapshot(WScreen *scr, WNativeWindow client_win, WNativeWindow frame_win) -{ - (void)frame_win; - struct wl_toplevel_view *v = wl_find_view_by_id(client_win); - if (!v) return NULL; - - /* XWayland: capture via XGetImage on the WM's X display */ - if (v->xw_surface && wl_state.x_display) { - Window xid = (Window)v->xw_surface->window_id; - XWindowAttributes attr; - if (!XGetWindowAttributes(wl_state.x_display, xid, &attr)) - return NULL; - XImage *img = XGetImage(wl_state.x_display, xid, - 0, 0, attr.width, attr.height, - AllPlanes, ZPixmap); - if (!img) return NULL; - RImage *res = RCreateImageFromXImage(scr->rcontext, img, NULL); - XDestroyImage(img); - return res; - } - - /* Native Wayland: read pixels from last committed surface buffer */ - if (!v->xdg_surface || !v->xdg_surface->surface) return NULL; - struct wlr_surface *s = v->xdg_surface->surface; - if (!s->buffer) return NULL; - struct wlr_buffer *buf = &s->buffer->base; - - uint32_t fmt; - void *data; - size_t stride; - if (!wlr_buffer_begin_data_ptr_access(buf, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &fmt, &stride)) - return NULL; - - int w = s->current.width; - int h = s->current.height; - if (w <= 0 || h <= 0) { - wlr_buffer_end_data_ptr_access(buf); - return NULL; - } - - /* Create RImage and copy pixels (ARGB32 → RGBA byte order swap) */ - RImage *res = RCreateImage(w, h, True); - if (res) { - unsigned char *src = (unsigned char *)data; - unsigned char *dst = res->data; - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - int si = y * stride + x * 4; - int di = (y * w + x) * 4; - dst[di + 0] = src[si + 2]; /* B → R */ - dst[di + 1] = src[si + 1]; /* G → G */ - dst[di + 2] = src[si + 0]; /* R → B */ - dst[di + 3] = src[si + 3]; /* A → A */ - } - } - } - - wlr_buffer_end_data_ptr_access(buf); - return res; -} static int wl_coords_translate(WNativeWindow src, WNativeWindow dst, diff --git a/src/backend/wayland/wl_client.c b/src/backend/wayland/wl_client.c new file mode 100644 index 0000000..9557808 --- /dev/null +++ b/src/backend/wayland/wl_client.c @@ -0,0 +1,489 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../screen.h" +#include "wl_types.h" +#include + +/* Forward declarations from sibling modules. */ +extern const struct wlr_buffer_impl frame_buf_impl; +void fb_attach_scene_buf_listener(struct wl_frame_buf *fb); + +struct wl_frame_buf *frame_buf_find(WNativeWindow id); +struct wl_toplevel_view *wl_find_view_by_id(WNativeWindow id); + +/* ------------------------------------------------------------------ */ +/* Client */ +/* ------------------------------------------------------------------ */ + +int +wl_window_get_client_attrs(WNativeWindow win, WClientAttributes *out) +{ + /* Phase 42: return real map_state and geometry from XDG surface. + * Phase 17: XWayland path uses xw_surface->mapped and width/height. */ + struct wl_toplevel_view *v = wl_find_view_by_id(win); + memset(out, 0, sizeof(*out)); + if (v && v->xw_surface) { + if (v->xw_surface->surface) { + out->map_state = WCLIENT_VIEWABLE; + out->width = v->xw_surface->width >= 64 ? v->xw_surface->width : 640; + out->height = v->xw_surface->height >= 64 ? v->xw_surface->height : 480; + } else { + out->map_state = WCLIENT_UNMAPPED; + } + } else if (v && v->xdg_surface && v->xdg_surface->surface) { + out->map_state = WCLIENT_VIEWABLE; + out->width = v->xdg_surface->surface->current.width; + out->height = v->xdg_surface->surface->current.height; + } else { + out->map_state = WCLIENT_UNMAPPED; + } + return 1; +} + +void +wl_client_get_visual_attrs(WNativeWindow win, WClientVisual *out) +{ + (void)win; + out->depth = 24; out->visual = NULL; out->colormap = 0; +} + +void +wl_client_adopt_attributes(WNativeWindow win, unsigned long event_mask) +{ (void)win; (void)event_mask; } + +void +wl_client_reparent(WNativeWindow client, WNativeWindow new_parent, int x, int y) +{ + /* Toplevel view reparented into a frame_buf — record the association + * so wl_window_move can keep them in sync. */ + struct wl_toplevel_view *v = wl_find_view_by_id(client); + if (v) { + v->frame_id = new_parent; + v->frame_rel_x = x; + v->frame_rel_y = y; + /* Position the client surface relative to the frame */ + struct wl_frame_buf *parent_fb = frame_buf_find(new_parent); + if (parent_fb) { + v->x = parent_fb->x + x; + v->y = parent_fb->y + y; + if (v->scene_node) { + /* Move client's scene tree under the frame's tree */ + if (parent_fb->tree) + wlr_scene_node_reparent(&v->scene_node->node, parent_fb->tree); + /* Position at relative offset within the tree */ + wlr_scene_node_set_position(&v->scene_node->node, x, y); + } + } + return; + } + + + + /* For WINGs internal windows: create a child frame_buf so the dialog + * content is visible in the scene graph. */ + if (client >= 0x20000000 && new_parent >= 0x10000000) { + /* client is a WINGs view ID, new_parent is a frame_buf */ + struct wl_frame_buf *parent_fb = frame_buf_find(new_parent); + if (!parent_fb) return; + + /* Check if we already have a frame_buf for this client */ + struct wl_frame_buf *existing = frame_buf_find(client); + if (existing) { + /* Just update parent and position */ + existing->parent_id = new_parent; + existing->rel_x = x; + existing->rel_y = y; + existing->x = parent_fb->x + x; + existing->y = parent_fb->y + y; + if (existing->scene_buf) { + /* Ensure it's under the parent frame's tree, then position + * at relative offset within the tree. */ + if (parent_fb->tree && + existing->scene_buf->node.parent != parent_fb->tree) { + wlr_scene_node_reparent(&existing->scene_buf->node, + parent_fb->tree); + } + if (parent_fb->tree) + wlr_scene_node_set_position(&existing->scene_buf->node, x, y); + else + wlr_scene_node_set_position(&existing->scene_buf->node, + existing->x, existing->y); + } + return; + } + + /* Create a new frame_buf for this WINGs view. + * Get size from the view itself. */ + extern W_View *W_GetViewForWindow(unsigned long id); + W_View *view = W_GetViewForWindow((unsigned long)client); + int w = view ? view->size.width : parent_fb->width; + int h = view ? view->size.height : (parent_fb->height - y); + if (w <= 0) w = 1; + if (h <= 0) h = 1; + + struct wl_frame_buf *fb = wmalloc(sizeof(*fb)); + memset(fb, 0, sizeof(*fb)); + + fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h, NULL, 0); + if (!fb->image) { wfree(fb); return; } + + wlr_buffer_init(&fb->base, &frame_buf_impl, w, h); + + fb->id = client; /* use the WINGs view ID directly */ + fb->parent_id = new_parent; + fb->rel_x = x; + fb->rel_y = y; + fb->x = parent_fb->x + x; + fb->y = parent_fb->y + y; + fb->width = w; + fb->height = h; + + if (wl_state.scene) { + /* Place the WINGs content under the parent frame's tree so it + * moves with the WM-managed dialog decorations. */ + struct wlr_scene_tree *_wp = parent_fb->tree + ? parent_fb->tree + : (wl_state.level_trees[WL_LAYER_FLOATING] + ? wl_state.level_trees[WL_LAYER_FLOATING] + : &wl_state.scene->tree); + fb->scene_buf = wlr_scene_buffer_create(_wp, &fb->base); + if (fb->scene_buf) { + fb_attach_scene_buf_listener(fb); + /* If under the frame tree, use relative coords; otherwise absolute. */ + if (parent_fb->tree) + wlr_scene_node_set_position(&fb->scene_buf->node, x, y); + else + wlr_scene_node_set_position(&fb->scene_buf->node, fb->x, fb->y); + wlr_scene_node_set_enabled(&fb->scene_buf->node, false); + } + } + + wl_list_insert(&wl_state.frame_list, &fb->link); + + /* Connect the WINGs view's backing store to this frame_buf's image */ + extern void W_SetViewBacking(unsigned long id, void *img); + W_SetViewBacking((unsigned long)client, fb->image); + } + /* Reparent an existing frame_buf into a new parent */ + else if (client >= 0x10000000 && new_parent >= 0x10000000) { + struct wl_frame_buf *fb = frame_buf_find(client); + struct wl_frame_buf *parent_fb = frame_buf_find(new_parent); + fprintf(stderr, "reparent_framebuf: client=0x%lx fb=%p new_parent=0x%lx parent_fb=%p\n", + (unsigned long)client, (void*)fb, + (unsigned long)new_parent, (void*)parent_fb); + if (fb) fprintf(stderr, " fb: tree=%p scene_buf=%p pos=(%d,%d)\n", + (void*)fb->tree, (void*)fb->scene_buf, + fb->tree ? fb->tree->node.x : -1, + fb->tree ? fb->tree->node.y : -1); + if (parent_fb) fprintf(stderr, " parent_fb: tree=%p pos=(%d,%d)\n", + (void*)parent_fb->tree, + parent_fb->tree ? parent_fb->tree->node.x : -1, + parent_fb->tree ? parent_fb->tree->node.y : -1); + if (fb && parent_fb) { + fb->parent_id = new_parent; + fb->rel_x = x; + fb->rel_y = y; + fb->x = parent_fb->x + x; + fb->y = parent_fb->y + y; + if (fb->tree) { + /* This frame_buf has a tree (e.g. fake_leader): reparent + * the whole tree so all children move with it. */ + if (parent_fb->tree) + wlr_scene_node_reparent(&fb->tree->node, parent_fb->tree); + wlr_scene_node_set_position(&fb->tree->node, x, y); + } else if (fb->scene_buf) { + /* Plain frame_buf (no tree): reparent scene_buf directly. */ + if (parent_fb->tree) + wlr_scene_node_reparent(&fb->scene_buf->node, parent_fb->tree); + wlr_scene_node_set_position(&fb->scene_buf->node, x, y); + } + /* Update absolute tracking recursively for all descendants */ + frame_buf_update_children_abs(client); + } + } +} + +/* Called by WINGs after a view repaints — commit its frame_buf to scene graph */ +void wl_view_commit_backing_impl(unsigned long id) +{ + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)id); + WNativeWindow saved_parent_id = 0; + int saved_rel_x = 0, saved_rel_y = 0; + /* If the view was resized, the old frame_buf's image is stale. */ + if (fb) { + extern void *W_GetViewBacking(unsigned long id); + void *current = W_GetViewBacking((unsigned long)id); + if (current != (void *)fb->image) { + /* Save parent relation before destroying — the new frame_buf + * must be positioned within the same parent tree so dialog + * content reparented via wl_client_reparent stays visible. */ + saved_parent_id = fb->parent_id; + saved_rel_x = fb->rel_x; + saved_rel_y = fb->rel_y; + wlr_scene_node_destroy(&fb->scene_buf->node); + wl_list_remove(&fb->link); + wlr_buffer_drop(&fb->base); + wfree(fb); + fb = NULL; + } + } + if (!fb) { + extern W_View *W_GetViewForWindow(unsigned long id); + extern void *W_GetViewBacking(unsigned long id); + W_View *view = W_GetViewForWindow((unsigned long)id); + if (view && view->flags.topLevel) { + fb = wmalloc(sizeof(*fb)); + memset(fb, 0, sizeof(*fb)); + fb->id = id; + fb->parent_id = saved_parent_id; + fb->rel_x = saved_rel_x; + fb->rel_y = saved_rel_y; + fb->x = view->pos.x; + fb->y = view->pos.y; + fb->width = view->size.width; + fb->height = view->size.height; + fb->image = (pixman_image_t *)W_GetViewBacking(id); + if (fb->image) { + pixman_image_ref(fb->image); + wlr_buffer_init(&fb->base, &frame_buf_impl, + pixman_image_get_width(fb->image), + pixman_image_get_height(fb->image)); + if (wl_state.scene) { + struct wlr_scene_tree *parent = NULL; + /* Re-parent under the saved parent's tree so + * dialogs reparented via wl_client_reparent stay + * correctly positioned within their frame. */ + if (saved_parent_id) { + struct wl_frame_buf *pfb = + frame_buf_find(saved_parent_id); + if (pfb && pfb->tree) + parent = pfb->tree; + } + if (!parent) + parent = wl_state.level_trees[WL_LAYER_FLOATING] + ? wl_state.level_trees[WL_LAYER_FLOATING] + : &wl_state.scene->tree; + fb->scene_buf = wlr_scene_buffer_create(parent, &fb->base); + if (fb->scene_buf) { + fb_attach_scene_buf_listener(fb); + /* Position relative to parent's tree or absolute */ + if (parent != &wl_state.scene->tree && saved_parent_id) + wlr_scene_node_set_position(&fb->scene_buf->node, + saved_rel_x, saved_rel_y); + else + wlr_scene_node_set_position(&fb->scene_buf->node, + fb->x, fb->y); + wlr_scene_node_set_enabled(&fb->scene_buf->node, false); + } + } + wl_list_insert(&wl_state.frame_list, &fb->link); + } else { + wfree(fb); + fb = NULL; + } + } + } + if (fb && fb->scene_buf) { + /* Sync position from the WINGs view — it may have moved since + * the frame_buf was created (e.g. switch panel is positioned + * after widget creation via WMMoveWidget). + * When reparented (parent_id set), use the relative offset + * instead of the view's absolute position. */ + extern W_View *W_GetViewForWindow(unsigned long id); + W_View *v = W_GetViewForWindow((unsigned long)id); + if (v && !fb->parent_id) { + if (fb->x != v->pos.x || fb->y != v->pos.y) { + fb->x = v->pos.x; + fb->y = v->pos.y; + wlr_scene_node_set_position(&fb->scene_buf->node, + fb->x, fb->y); + } + } + /* Only enable the scene node if the view is still mapped. + * If the view was unmapped (e.g. move ended), wl_unmap disables + * it via W_ViewSceneNodeSetEnabledFn — don't re-enable here. */ + if (!v || v->flags.mapped) { + wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); + wlr_scene_node_set_enabled(&fb->scene_buf->node, true); + } + } +} +void +wl_view_scene_node_set_enabled(unsigned long id, int enabled) +{ + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)id); + if (fb && fb->scene_buf) + wlr_scene_node_set_enabled(&fb->scene_buf->node, !!enabled); +} + + +void wl_view_destroy_backing_impl(unsigned long id) +{ + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)id); + if (!fb) return; + wl_list_remove(&fb->link); + /* fb->scene_buf may have been auto-destroyed by wlroots already if a + * parent tree was destroyed earlier (e.g. wl_frame_destroy on the + * surrounding WMaker frame). The destroy listener NULLs scene_buf + * in that case. */ + if (fb->scene_buf) + wlr_scene_node_destroy(&fb->scene_buf->node); + wlr_buffer_drop(&fb->base); +} + +void +wl_client_synth_configure(WNativeWindow client_win, + int x, int y, int width, int height, + int border_width, WNativeWindow above) +{ + /* Phase 44: send an XDG configure with the new size to the client. + * Phase 13: also record the WM-assigned position in the view so + * pointer hit-testing (view_surface_at) uses current coordinates. + * Phase 17: XWayland uses wlr_xwayland_surface_configure instead. */ + (void)border_width; (void)above; + struct wl_toplevel_view *v = wl_find_view_by_id(client_win); + if (!v) return; + v->x = x; + v->y = y; + /* Update relative offset so frame_configure keeps us in sync. */ + if (v->frame_id) { + struct wl_frame_buf *pfb = frame_buf_find(v->frame_id); + if (pfb) { + v->frame_rel_x = x - pfb->x; + v->frame_rel_y = y - pfb->y; + } + /* Scene node is child of frame tree — use relative coords */ + if (v->scene_node) + wlr_scene_node_set_position(&v->scene_node->node, + v->frame_rel_x, v->frame_rel_y); + } else { + /* No frame — use absolute coords */ + if (v->scene_node) + wlr_scene_node_set_position(&v->scene_node->node, x, y); + } + if (v->xw_surface) { + wlr_xwayland_surface_configure(v->xw_surface, + (int16_t)x, (int16_t)y, + width > 0 ? (uint16_t)width : v->xw_surface->width, + height > 0 ? (uint16_t)height : v->xw_surface->height); + return; + } + if (!v->xdg_surface || !v->xdg_surface->toplevel) return; + if (width > 0 && height > 0) + wlr_xdg_toplevel_set_size(v->xdg_surface->toplevel, (uint32_t)width, (uint32_t)height); + wlr_xdg_surface_schedule_configure(v->xdg_surface); +} + +void wl_client_add_to_save_set(WNativeWindow win) { (void)win; } +void wl_client_remove_from_save_set(WNativeWindow win) { (void)win; } + +void +wl_client_close(WNativeWindow win) +{ + /* Phase 46: ask the surface to close. + * Phase 17: XWayland uses wlr_xwayland_surface_close. */ + struct wl_toplevel_view *v = wl_find_view_by_id(win); + if (!v) return; + if (v->xw_surface) { + wlr_xwayland_surface_close(v->xw_surface); + } else if (v->xdg_surface && v->xdg_surface->toplevel) { + wlr_xdg_toplevel_send_close(v->xdg_surface->toplevel); + /* Flush so the in-process client receives the close event, + * then dispatch so it processes it on this iteration. */ + wl_display_flush_clients(wl_state.display); + } +} + +void wl_client_query_shaped(WNativeWindow win, int *shaped_out) +{ (void)win; *shaped_out = 0; } + +int +wl_client_get_gnustep_attrs(WNativeWindow win, GNUstepWMAttributes **attr_out) +{ (void)win; *attr_out = NULL; return 0; } + +int wl_client_get_pid(WNativeWindow win) +{ + struct wl_toplevel_view *v = wl_find_view_by_id(win); + if (!v) return 0; + /* Phase 17: XWayland surfaces have a direct pid field. */ + if (v->xw_surface) + return (int)v->xw_surface->pid; + if (!v->xdg_surface || !v->xdg_surface->resource) + return 0; + struct wl_client *client = wl_resource_get_client(v->xdg_surface->resource); + if (!client) return 0; + pid_t pid = 0; uid_t uid = 0; gid_t gid = 0; + wl_client_get_credentials(client, &pid, &uid, &gid); + return (int)pid; +} + +RImage * +wl_client_snapshot(WScreen *scr, WNativeWindow client_win, WNativeWindow frame_win) +{ + (void)frame_win; + struct wl_toplevel_view *v = wl_find_view_by_id(client_win); + if (!v) return NULL; + + /* XWayland: capture via XGetImage on the WM's X display */ + if (v->xw_surface && wl_state.x_display) { + Window xid = (Window)v->xw_surface->window_id; + XWindowAttributes attr; + if (!XGetWindowAttributes(wl_state.x_display, xid, &attr)) + return NULL; + XImage *img = XGetImage(wl_state.x_display, xid, + 0, 0, attr.width, attr.height, + AllPlanes, ZPixmap); + if (!img) return NULL; + RImage *res = RCreateImageFromXImage(scr->rcontext, img, NULL); + XDestroyImage(img); + return res; + } + + /* Native Wayland: read pixels from last committed surface buffer */ + if (!v->xdg_surface || !v->xdg_surface->surface) return NULL; + struct wlr_surface *s = v->xdg_surface->surface; + if (!s->buffer) return NULL; + struct wlr_buffer *buf = &s->buffer->base; + + uint32_t fmt; + void *data; + size_t stride; + if (!wlr_buffer_begin_data_ptr_access(buf, WLR_BUFFER_DATA_PTR_ACCESS_READ, &data, &fmt, &stride)) + return NULL; + + int w = s->current.width; + int h = s->current.height; + if (w <= 0 || h <= 0) { + wlr_buffer_end_data_ptr_access(buf); + return NULL; + } + + /* Create RImage and copy pixels (ARGB32 → RGBA byte order swap) */ + RImage *res = RCreateImage(w, h, True); + if (res) { + unsigned char *src = (unsigned char *)data; + unsigned char *dst = res->data; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int si = y * stride + x * 4; + int di = (y * w + x) * 4; + dst[di + 0] = src[si + 2]; /* B → R */ + dst[di + 1] = src[si + 1]; /* G → G */ + dst[di + 2] = src[si + 0]; /* R → B */ + dst[di + 3] = src[si + 3]; /* A → A */ + } + } + } + + wlr_buffer_end_data_ptr_access(buf); + return res; +} diff --git a/src/backend/wayland/wl_types.h b/src/backend/wayland/wl_types.h index 47d0edc..09ddcb8 100644 --- a/src/backend/wayland/wl_types.h +++ b/src/backend/wayland/wl_types.h @@ -461,3 +461,17 @@ int wl_prop_has_wmaker_menu(WNativeWindow win); int wl_prop_has_gtk_object_path(WNativeWindow win); void wl_prop_set_wm_state(WNativeWindow win, const long *data, int n); int wl_prop_get_wm_state(WNativeWindow win, long **data_out); + +/* Client module declarations (extracted to wl_client.c) */ +int wl_window_get_client_attrs(WNativeWindow win, WClientAttributes *out); +void wl_client_get_visual_attrs(WNativeWindow win, WClientVisual *out); +void wl_client_adopt_attributes(WNativeWindow win, unsigned long event_mask); +void wl_client_reparent(WNativeWindow client, WNativeWindow new_parent, int x, int y); +void wl_client_synth_configure(WNativeWindow client_win, int x, int y, int width, int height, int border_width, WNativeWindow above); +void wl_client_add_to_save_set(WNativeWindow win); +void wl_client_remove_from_save_set(WNativeWindow win); +void wl_client_close(WNativeWindow win); +void wl_client_query_shaped(WNativeWindow win, int *shaped_out); +int wl_client_get_gnustep_attrs(WNativeWindow win, GNUstepWMAttributes **attr_out); +int wl_client_get_pid(WNativeWindow win); +RImage *wl_client_snapshot(WScreen *scr, WNativeWindow client_win, WNativeWindow frame_win);