377 lines
15 KiB
C
377 lines
15 KiB
C
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <wayland-server-core.h>
|
|
#include <wlr/types/wlr_scene.h>
|
|
#include <wlr/types/wlr_xdg_shell.h>
|
|
#include <wlr/xwayland.h>
|
|
#include <xcb/xproto.h>
|
|
#include <X11/Xlib.h>
|
|
#include "../../window.h"
|
|
#include "wl_types.h"
|
|
|
|
if (fb) {
|
|
if (win_x) { *win_x = fb->x; }
|
|
if (win_y) { *win_y = fb->y; }
|
|
if (win_w) { *win_w = fb->width; }
|
|
if (win_h) { *win_h = fb->height; }
|
|
return 1;
|
|
}
|
|
/* Synthetic child IDs (titlebar, buttons) — return minimal geometry. */
|
|
if (win_x) { *win_x = 0; } if (win_y) { *win_y = 0; }
|
|
if (win_w) { *win_w = 1; } if (win_h) { *win_h = 1; }
|
|
return 0;
|
|
}
|
|
|
|
void wl_window_add_events(WNativeWindow win, unsigned long extra_mask) { (void)win; (void)extra_mask; }
|
|
void wl_window_select_events(WNativeWindow win, unsigned long mask) { (void)win; (void)mask; }
|
|
|
|
void wl_window_shape_clear(WWindow *wwin) { (void)wwin; }
|
|
void wl_window_shape_set(WWindow *wwin) { (void)wwin; }
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* Overlays */
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
void
|
|
wl_overlay_draw_line(WScreen *scr, int x1, int y1, int x2, int y2)
|
|
{
|
|
overlay_ensure(scr);
|
|
if (wl_state.overlay_surf) {
|
|
ovl_line(wl_state.overlay_surf, x1, y1, x2, y2);
|
|
overlay_flush();
|
|
}
|
|
}
|
|
|
|
void
|
|
wl_overlay_draw_lines(WScreen *scr, WPoint *pts, int n)
|
|
{
|
|
overlay_ensure(scr);
|
|
struct wm_shm_surface *s = wl_state.overlay_surf;
|
|
if (!s || n < 2) return;
|
|
for (int i = 0; i < n - 1; i++)
|
|
ovl_line(s, pts[i].x, pts[i].y, pts[i+1].x, pts[i+1].y);
|
|
}
|
|
|
|
void
|
|
wl_overlay_draw_rect(WScreen *scr, int x, int y, int w, int h)
|
|
{
|
|
overlay_ensure(scr);
|
|
struct wm_shm_surface *s = wl_state.overlay_surf;
|
|
if (!s || w <= 0 || h <= 0) return;
|
|
/* Matching XDrawRectangle semantics: corners are shared between
|
|
* adjacent edges and therefore XOR'd twice (become transparent). */
|
|
ovl_hline(s, x, y, w + 1); /* top */
|
|
ovl_hline(s, x, y + h, w + 1); /* bottom */
|
|
ovl_vline(s, x, y, h + 1); /* left */
|
|
ovl_vline(s, x + w, y, h + 1); /* right */
|
|
overlay_flush();
|
|
}
|
|
|
|
void
|
|
wl_overlay_draw_segments(WScreen *scr, WSegment *segs, int n)
|
|
{
|
|
overlay_ensure(scr);
|
|
struct wm_shm_surface *s = wl_state.overlay_surf;
|
|
if (!s) return;
|
|
for (int i = 0; i < n; i++)
|
|
ovl_line(s, segs[i].x1, segs[i].y1, segs[i].x2, segs[i].y2);
|
|
}
|
|
|
|
void
|
|
wl_overlay_draw_string(WScreen *scr, int x, int y, const char *str, int len)
|
|
{
|
|
overlay_ensure(scr);
|
|
struct wm_shm_surface *s = wl_state.overlay_surf;
|
|
if (!s || !str) return;
|
|
/* y is the text baseline; render from the top of the cell. */
|
|
int top = y - (OVL_GH - 2);
|
|
for (int i = 0; i < len; i++)
|
|
ovl_draw_glyph(s, x + i * OVL_CW, top, (unsigned char)str[i]);
|
|
}
|
|
|
|
int
|
|
wl_overlay_text_width(WScreen *scr, const char *str, int n)
|
|
{
|
|
(void)scr; (void)str;
|
|
return n * OVL_CW;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
/* 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).
|