decompose wl_backend.c: extract event, stacking, overlay modules
This commit is contained in:
parent
758623269b
commit
5175cf880c
|
|
@ -158,7 +158,10 @@ if USE_WAYLAND_BACKEND
|
|||
wmaker_SOURCES += \
|
||||
backend/wayland/wl_backend.h \
|
||||
backend/wayland/wl_backend.c \
|
||||
backend/wayland/wl_context.c
|
||||
backend/wayland/wl_context.c \
|
||||
backend/wayland/wl_event.c \
|
||||
backend/wayland/wl_stacking.c \
|
||||
backend/wayland/wl_overlay.c
|
||||
endif
|
||||
|
||||
if USE_NESTED_FUNC
|
||||
|
|
|
|||
|
|
@ -238,9 +238,9 @@ static void wm_dock_app_remove(WNativeWindow id);
|
|||
struct wl_compositor_state wl_state;
|
||||
|
||||
/* Forward declarations for helpers used before their definition. */
|
||||
static int level_to_bucket(int level);
|
||||
static struct wlr_scene_tree *level_tree_for(int level);
|
||||
static struct wl_frame_buf *frame_buf_find(WNativeWindow id);
|
||||
int level_to_bucket(int level);
|
||||
struct wlr_scene_tree *level_tree_for(int level);
|
||||
struct wl_frame_buf *frame_buf_find(WNativeWindow id);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Phase A: WINGs event backend wiring helpers */
|
||||
|
|
@ -384,7 +384,7 @@ static void handle_pointer_button(struct wl_listener *listener, void *data);
|
|||
static void handle_pointer_frame(struct wl_listener *listener, void *data);
|
||||
|
||||
/* Phase 41: look up the view for a given synthetic Window ID. */
|
||||
static struct wl_toplevel_view *
|
||||
struct wl_toplevel_view *
|
||||
wl_find_view_by_id(WNativeWindow id)
|
||||
{
|
||||
struct wl_toplevel_view *v;
|
||||
|
|
@ -3439,19 +3439,7 @@ wl_event_loop_terminate(void)
|
|||
* once the dock and title-bar code drives it.
|
||||
*/
|
||||
|
||||
struct wm_shm_surface {
|
||||
/* wlr_buffer interface — MUST be first so wl_container_of works */
|
||||
struct wlr_buffer wlr_buf;
|
||||
/* Pixel store */
|
||||
uint32_t *data; /* ARGB8888, row-major */
|
||||
int width;
|
||||
int height;
|
||||
size_t stride; /* bytes per row = width * 4 */
|
||||
/* pixman drawing surface wrapping data[] */
|
||||
pixman_image_t *image;
|
||||
/* scene graph presence */
|
||||
struct wlr_scene_buffer *scene_buf; /* NULL until first commit */
|
||||
};
|
||||
/* struct wm_shm_surface now in wl_types.h */
|
||||
|
||||
/* ---------- wlr_buffer implementation -------------------------------- */
|
||||
|
||||
|
|
@ -3491,7 +3479,7 @@ static const struct wlr_buffer_impl wm_shm_buf_impl = {
|
|||
|
||||
/* ---------- surface API ---------------------------------------------- */
|
||||
|
||||
static struct wm_shm_surface *
|
||||
struct wm_shm_surface *
|
||||
wm_shm_surface_create(int width, int height)
|
||||
{
|
||||
if (width <= 0 || height <= 0)
|
||||
|
|
@ -3531,7 +3519,7 @@ wm_shm_surface_create(int width, int height)
|
|||
* Fill a rectangle with a solid ARGB colour.
|
||||
* argb: 0xAARRGGBB — e.g. 0xFF4080C0 for opaque steel-blue.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
wm_shm_surface_fill(struct wm_shm_surface *s,
|
||||
int x, int y, int w, int h,
|
||||
uint32_t argb)
|
||||
|
|
@ -3570,7 +3558,7 @@ wm_shm_surface_fill(struct wm_shm_surface *s,
|
|||
* reposition it (pixel updates are uploaded lazily by wlroots on the
|
||||
* next wlr_scene_output_commit).
|
||||
*/
|
||||
static void
|
||||
void
|
||||
wm_shm_surface_commit(struct wm_shm_surface *s,
|
||||
struct wlr_scene_tree *parent,
|
||||
int x, int y)
|
||||
|
|
@ -3594,7 +3582,7 @@ wm_shm_surface_commit(struct wm_shm_surface *s,
|
|||
* After this call s is invalid — the memory is freed asynchronously
|
||||
* by shm_buf_destroy once the scene node releases its lock.
|
||||
*/
|
||||
static void
|
||||
void
|
||||
wm_shm_surface_destroy(struct wm_shm_surface *s)
|
||||
{
|
||||
if (!s)
|
||||
|
|
@ -4020,7 +4008,7 @@ static const struct wlr_buffer_impl frame_buf_impl = {
|
|||
.end_data_ptr_access = frame_buf_end_data_ptr_access,
|
||||
};
|
||||
|
||||
static struct wl_frame_buf *
|
||||
struct wl_frame_buf *
|
||||
frame_buf_find(WNativeWindow id)
|
||||
{
|
||||
struct wl_frame_buf *fb;
|
||||
|
|
@ -6261,454 +6249,11 @@ wl_keyboard_ungrab(void)
|
|||
wl_state.keyboard_grabbed = false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Phase 81: Overlay animation surface — XOR pixel engine + bitmap */
|
||||
/* font for geometry display (miniaturize zoom, transparent */
|
||||
/* move/resize frames, position/size readouts). */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* XOR mask: 0xFFFFFFFF XOR 0x00000000 (transparent) = 0xFFFFFFFF (opaque
|
||||
* white); XOR 0xFFFFFFFF again = 0x00000000 (transparent, i.e. erased).
|
||||
* The overlay scene buffer is composited OVER all client windows via
|
||||
* standard alpha blending, so transparent pixels show through to the
|
||||
* desktop and opaque-white pixels appear on top.
|
||||
*/
|
||||
#define OVL_MASK 0xFFFFFFFFu
|
||||
|
||||
/*
|
||||
* 5-wide × 9-tall bitmap font for printable ASCII (0x20–0x7E).
|
||||
* Encoding: bit 7 = column 0 (left), bit 3 = column 4 (right), bits 2–0
|
||||
* unused. Cell width = 6 px (glyph 5 + 1 gap); cell height = 10 px.
|
||||
*/
|
||||
#define OVL_GW 5 /* glyph pixel width */
|
||||
#define OVL_GH 9 /* glyph pixel height */
|
||||
#define OVL_CW 6 /* cell width (px) */
|
||||
#define OVL_CH 10 /* cell height (px) */
|
||||
|
||||
static const uint8_t ovl_font[96][OVL_GH] = {
|
||||
/*20 ' '*/{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/*21 '!'*/{0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00},
|
||||
/*22 '"'*/{0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/*23 '#'*/{0x50,0x50,0xF8,0x50,0xF8,0x50,0x50,0x00,0x00},
|
||||
/*24 '$'*/{0x20,0x70,0xA0,0x70,0x28,0x70,0x20,0x00,0x00},
|
||||
/*25 '%'*/{0xC0,0xC8,0x10,0x20,0x40,0x98,0x18,0x00,0x00},
|
||||
/*26 '&'*/{0x60,0x90,0x60,0xA0,0x98,0x90,0x68,0x00,0x00},
|
||||
/*27'\''*/ {0x60,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/*28 '('*/{0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00,0x00},
|
||||
/*29 ')'*/{0x40,0x20,0x10,0x10,0x10,0x20,0x40,0x00,0x00},
|
||||
/*2A '*'*/{0x00,0x88,0x50,0xF8,0x50,0x88,0x00,0x00,0x00},
|
||||
/*2B '+'*/{0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00,0x00},
|
||||
/*2C ','*/{0x00,0x00,0x00,0x00,0x60,0x20,0x40,0x00,0x00},
|
||||
/*2D '-'*/{0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00},
|
||||
/*2E '.'*/{0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00},
|
||||
/*2F '/'*/{0x08,0x08,0x10,0x20,0x40,0x80,0x80,0x00,0x00},
|
||||
/*30 '0'*/{0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00},
|
||||
/*31 '1'*/{0x20,0x60,0x20,0x20,0x20,0x20,0x70,0x00,0x00},
|
||||
/*32 '2'*/{0x70,0x88,0x08,0x30,0x40,0x80,0xF8,0x00,0x00},
|
||||
/*33 '3'*/{0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00,0x00},
|
||||
/*34 '4'*/{0x10,0x30,0x50,0x90,0xF8,0x10,0x10,0x00,0x00},
|
||||
/*35 '5'*/{0xF8,0x80,0xF0,0x08,0x08,0x88,0x70,0x00,0x00},
|
||||
/*36 '6'*/{0x38,0x40,0x80,0xF0,0x88,0x88,0x70,0x00,0x00},
|
||||
/*37 '7'*/{0xF8,0x08,0x10,0x20,0x20,0x20,0x20,0x00,0x00},
|
||||
/*38 '8'*/{0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x00},
|
||||
/*39 '9'*/{0x70,0x88,0x88,0x78,0x08,0x08,0x70,0x00,0x00},
|
||||
/*3A ':'*/{0x00,0x60,0x60,0x00,0x60,0x60,0x00,0x00,0x00},
|
||||
/*3B ';'*/{0x00,0x60,0x60,0x00,0x60,0x20,0x40,0x00,0x00},
|
||||
/*3C '<'*/{0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00,0x00},
|
||||
/*3D '='*/{0x00,0x00,0xF8,0x00,0xF8,0x00,0x00,0x00,0x00},
|
||||
/*3E '>'*/{0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x00},
|
||||
/*3F '?'*/{0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00,0x00},
|
||||
/*40 '@'*/{0x70,0x88,0x98,0xA8,0x98,0x80,0x70,0x00,0x00},
|
||||
/*41 'A'*/{0x20,0x50,0x88,0x88,0xF8,0x88,0x88,0x00,0x00},
|
||||
/*42 'B'*/{0xF0,0x88,0x88,0xF0,0x88,0x88,0xF0,0x00,0x00},
|
||||
/*43 'C'*/{0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00,0x00},
|
||||
/*44 'D'*/{0xE0,0x90,0x88,0x88,0x88,0x90,0xE0,0x00,0x00},
|
||||
/*45 'E'*/{0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8,0x00,0x00},
|
||||
/*46 'F'*/{0xF8,0x80,0x80,0xF0,0x80,0x80,0x80,0x00,0x00},
|
||||
/*47 'G'*/{0x70,0x88,0x80,0xB8,0x88,0x88,0x70,0x00,0x00},
|
||||
/*48 'H'*/{0x88,0x88,0x88,0xF8,0x88,0x88,0x88,0x00,0x00},
|
||||
/*49 'I'*/{0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00},
|
||||
/*4A 'J'*/{0x38,0x10,0x10,0x10,0x10,0x90,0x60,0x00,0x00},
|
||||
/*4B 'K'*/{0x88,0x90,0xA0,0xC0,0xA0,0x90,0x88,0x00,0x00},
|
||||
/*4C 'L'*/{0x80,0x80,0x80,0x80,0x80,0x80,0xF8,0x00,0x00},
|
||||
/*4D 'M'*/{0x88,0xD8,0xA8,0x88,0x88,0x88,0x88,0x00,0x00},
|
||||
/*4E 'N'*/{0x88,0xC8,0xA8,0x98,0x88,0x88,0x88,0x00,0x00},
|
||||
/*4F 'O'*/{0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00},
|
||||
/*50 'P'*/{0xF0,0x88,0x88,0xF0,0x80,0x80,0x80,0x00,0x00},
|
||||
/*51 'Q'*/{0x70,0x88,0x88,0x88,0xA8,0x90,0x68,0x00,0x00},
|
||||
/*52 'R'*/{0xF0,0x88,0x88,0xF0,0xA0,0x90,0x88,0x00,0x00},
|
||||
/*53 'S'*/{0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00,0x00},
|
||||
/*54 'T'*/{0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00},
|
||||
/*55 'U'*/{0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00},
|
||||
/*56 'V'*/{0x88,0x88,0x88,0x88,0x50,0x50,0x20,0x00,0x00},
|
||||
/*57 'W'*/{0x88,0x88,0x88,0xA8,0xA8,0xD8,0x88,0x00,0x00},
|
||||
/*58 'X'*/{0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x00},
|
||||
/*59 'Y'*/{0x88,0x88,0x50,0x20,0x20,0x20,0x20,0x00,0x00},
|
||||
/*5A 'Z'*/{0xF8,0x08,0x10,0x20,0x40,0x80,0xF8,0x00,0x00},
|
||||
/*5B '['*/{0x70,0x40,0x40,0x40,0x40,0x40,0x70,0x00,0x00},
|
||||
/*5C'\\'*/ {0x80,0x80,0x40,0x20,0x10,0x08,0x08,0x00,0x00},
|
||||
/*5D ']'*/{0x70,0x10,0x10,0x10,0x10,0x10,0x70,0x00,0x00},
|
||||
/*5E '^'*/{0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/*5F '_'*/{0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x00,0x00},
|
||||
/*60 '`'*/{0x40,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/*61 'a'*/{0x00,0x00,0x70,0x08,0x78,0x88,0x78,0x00,0x00},
|
||||
/*62 'b'*/{0x80,0x80,0xF0,0x88,0x88,0x88,0xF0,0x00,0x00},
|
||||
/*63 'c'*/{0x00,0x00,0x70,0x88,0x80,0x88,0x70,0x00,0x00},
|
||||
/*64 'd'*/{0x08,0x08,0x78,0x88,0x88,0x88,0x78,0x00,0x00},
|
||||
/*65 'e'*/{0x00,0x00,0x70,0x88,0xF8,0x80,0x70,0x00,0x00},
|
||||
/*66 'f'*/{0x30,0x48,0x40,0xE0,0x40,0x40,0x40,0x00,0x00},
|
||||
/*67 'g'*/{0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x70,0x00},
|
||||
/*68 'h'*/{0x80,0x80,0xF0,0x88,0x88,0x88,0x88,0x00,0x00},
|
||||
/*69 'i'*/{0x20,0x00,0x60,0x20,0x20,0x20,0x70,0x00,0x00},
|
||||
/*6A 'j'*/{0x10,0x00,0x30,0x10,0x10,0x10,0x90,0x60,0x00},
|
||||
/*6B 'k'*/{0x80,0x80,0x90,0xA0,0xC0,0xA0,0x90,0x00,0x00},
|
||||
/*6C 'l'*/{0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00},
|
||||
/*6D 'm'*/{0x00,0x00,0xD8,0xA8,0xA8,0xA8,0x88,0x00,0x00},
|
||||
/*6E 'n'*/{0x00,0x00,0xF0,0x88,0x88,0x88,0x88,0x00,0x00},
|
||||
/*6F 'o'*/{0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00,0x00},
|
||||
/*70 'p'*/{0x00,0x00,0xF0,0x88,0x88,0xF0,0x80,0x80,0x00},
|
||||
/*71 'q'*/{0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x08,0x00},
|
||||
/*72 'r'*/{0x00,0x00,0xB0,0xC8,0x80,0x80,0x80,0x00,0x00},
|
||||
/*73 's'*/{0x00,0x00,0x70,0x80,0x70,0x08,0xF0,0x00,0x00},
|
||||
/*74 't'*/{0x40,0x40,0xF0,0x40,0x40,0x48,0x30,0x00,0x00},
|
||||
/*75 'u'*/{0x00,0x00,0x88,0x88,0x88,0x98,0x68,0x00,0x00},
|
||||
/*76 'v'*/{0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00,0x00},
|
||||
/*77 'w'*/{0x00,0x00,0x88,0x88,0xA8,0xD8,0x88,0x00,0x00},
|
||||
/*78 'x'*/{0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00},
|
||||
/*79 'y'*/{0x00,0x00,0x88,0x88,0x78,0x08,0x70,0x00,0x00},
|
||||
/*7A 'z'*/{0x00,0x00,0xF8,0x10,0x20,0x40,0xF8,0x00,0x00},
|
||||
/*7B '{'*/{0x18,0x20,0x20,0x40,0x20,0x20,0x18,0x00,0x00},
|
||||
/*7C '|'*/{0x20,0x20,0x20,0x00,0x20,0x20,0x20,0x00,0x00},
|
||||
/*7D '}'*/{0x60,0x10,0x10,0x08,0x10,0x10,0x60,0x00,0x00},
|
||||
/*7E '~'*/{0x40,0xA8,0x18,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
};
|
||||
|
||||
/* Lazily allocate (or resize) the full-screen overlay buffer. */
|
||||
static void
|
||||
overlay_ensure(WScreen *scr)
|
||||
{
|
||||
int w, h;
|
||||
if (scr && scr->scr_width > 0 && scr->scr_height > 0) {
|
||||
w = scr->scr_width;
|
||||
h = scr->scr_height;
|
||||
} else if (wl_state.output) {
|
||||
w = (int)wl_state.output->width;
|
||||
h = (int)wl_state.output->height;
|
||||
} else {
|
||||
w = 1920; h = 1080;
|
||||
}
|
||||
if (wl_state.overlay_surf &&
|
||||
wl_state.overlay_surf->width == w &&
|
||||
wl_state.overlay_surf->height == h)
|
||||
return;
|
||||
if (wl_state.overlay_surf) {
|
||||
wm_shm_surface_destroy(wl_state.overlay_surf);
|
||||
wl_state.overlay_surf = NULL;
|
||||
}
|
||||
wl_state.overlay_surf = wm_shm_surface_create(w, h);
|
||||
}
|
||||
|
||||
/* XOR a single pixel on the overlay buffer (clipped). */
|
||||
static inline void
|
||||
ovl_plot(struct wm_shm_surface *s, int x, int y)
|
||||
{
|
||||
if ((unsigned)x < (unsigned)s->width && (unsigned)y < (unsigned)s->height)
|
||||
s->data[y * (int)(s->stride >> 2) + x] ^= OVL_MASK;
|
||||
}
|
||||
|
||||
/* XOR a horizontal run of `len` pixels starting at (x, y). */
|
||||
static void
|
||||
ovl_hline(struct wm_shm_surface *s, int x, int y, int len)
|
||||
{
|
||||
if ((unsigned)y >= (unsigned)s->height || len <= 0) return;
|
||||
int x0 = x < 0 ? 0 : x;
|
||||
int x1 = x + len; if (x1 > s->width) x1 = s->width;
|
||||
if (x0 >= x1) return;
|
||||
uint32_t *p = s->data + y * (int)(s->stride >> 2) + x0;
|
||||
for (int i = x0; i < x1; i++) *p++ ^= OVL_MASK;
|
||||
}
|
||||
|
||||
/* XOR a vertical run of `len` pixels starting at (x, y). */
|
||||
static void
|
||||
ovl_vline(struct wm_shm_surface *s, int x, int y, int len)
|
||||
{
|
||||
if ((unsigned)x >= (unsigned)s->width || len <= 0) return;
|
||||
int y0 = y < 0 ? 0 : y;
|
||||
int y1 = y + len; if (y1 > s->height) y1 = s->height;
|
||||
int stride = (int)(s->stride >> 2);
|
||||
for (int j = y0; j < y1; j++)
|
||||
s->data[j * stride + x] ^= OVL_MASK;
|
||||
}
|
||||
|
||||
/* Bresenham line (with fast h/v paths). */
|
||||
static void
|
||||
ovl_line(struct wm_shm_surface *s, int x0, int y0, int x1, int y1)
|
||||
{
|
||||
if (x0 == x1) {
|
||||
int y = y0 < y1 ? y0 : y1;
|
||||
ovl_vline(s, x0, y, abs(y1 - y0) + 1);
|
||||
return;
|
||||
}
|
||||
if (y0 == y1) {
|
||||
int x = x0 < x1 ? x0 : x1;
|
||||
ovl_hline(s, x, y0, abs(x1 - x0) + 1);
|
||||
return;
|
||||
}
|
||||
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||
int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx + dy;
|
||||
for (;;) {
|
||||
ovl_plot(s, x0, y0);
|
||||
if (x0 == x1 && y0 == y1) break;
|
||||
int e2 = 2 * err;
|
||||
if (e2 >= dy) { err += dy; x0 += sx; }
|
||||
if (e2 <= dx) { err += dx; y0 += sy; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Render one glyph at cell origin (cx, cy) using XOR. */
|
||||
static void
|
||||
ovl_draw_glyph(struct wm_shm_surface *s, int cx, int cy, unsigned char ch)
|
||||
{
|
||||
if (ch < 0x20 || ch > 0x7E) ch = '?';
|
||||
const uint8_t *g = ovl_font[ch - 0x20];
|
||||
for (int row = 0; row < OVL_GH; row++) {
|
||||
uint8_t bits = g[row];
|
||||
if (!bits) continue;
|
||||
int py = cy + row;
|
||||
if (py < 0 || py >= s->height) continue;
|
||||
for (int col = 0; col < OVL_GW; col++) {
|
||||
if (bits & (0x80u >> col))
|
||||
ovl_plot(s, cx + col, py);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Push the modified overlay buffer to the scene and render a frame. */
|
||||
static void
|
||||
overlay_flush(void)
|
||||
{
|
||||
struct wm_shm_surface *s = wl_state.overlay_surf;
|
||||
if (!s || !wl_state.scene) return;
|
||||
/* First call creates the scene_buffer node; subsequent calls reposition. */
|
||||
/* Overlay surface lives in WL_LAYER_OVERLAY so it is structurally
|
||||
* above every window and dock node without needing raise_to_top. */
|
||||
{
|
||||
struct wlr_scene_tree *_op = wl_state.level_trees[WL_LAYER_OVERLAY]
|
||||
? wl_state.level_trees[WL_LAYER_OVERLAY] : &wl_state.scene->tree;
|
||||
wm_shm_surface_commit(s, _op, 0, 0);
|
||||
}
|
||||
if (s->scene_buf) {
|
||||
/* Ensure the node is visible for the animation frame. */
|
||||
wlr_scene_node_set_enabled(&s->scene_buf->node, true);
|
||||
/* Notify wlroots that the pixel data has changed. */
|
||||
wlr_scene_buffer_set_buffer_with_damage(s->scene_buf, &s->wlr_buf, NULL);
|
||||
}
|
||||
/* Commit the scene immediately so the animation step is visible
|
||||
* before the caller's wusleep() delay. */
|
||||
if (wl_state.scene_output && wl_state.allocator)
|
||||
wlr_scene_output_commit(wl_state.scene_output, NULL);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Server grab */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void wl_server_grab(void) {} /* no-op: Wayland double-buffers for us */
|
||||
static void wl_server_ungrab(void)
|
||||
{
|
||||
overlay_flush();
|
||||
/* Disable the overlay scene node so it doesn't intercept hit-testing.
|
||||
* The XOR animation has already erased its pixels; leaving the node
|
||||
* enabled blocks frame_buf_at (wlr_scene_node_at hits the overlay
|
||||
* before any frame_buf). */
|
||||
if (wl_state.overlay_surf && wl_state.overlay_surf->scene_buf)
|
||||
wlr_scene_node_set_enabled(&wl_state.overlay_surf->scene_buf->node, false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Stacking */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* Map a WM window level integer to a level_trees[] bucket index.
|
||||
* Buckets are ordered 0=lowest (WL_LAYER_BACKGROUND) to 8=highest
|
||||
* (WL_LAYER_OVERLAY), matching the bottom-to-top creation order of
|
||||
* level_trees[]. Does not map WL_LAYER_BACKGROUND (no WM level). */
|
||||
static int
|
||||
level_to_bucket(int level)
|
||||
{
|
||||
if (level < -1) return WL_LAYER_DESKTOP;
|
||||
if (level < 0) return WL_LAYER_SUNKEN;
|
||||
if (level < 3) return WL_LAYER_NORMAL;
|
||||
if (level < 20) return WL_LAYER_FLOATING;
|
||||
if (level < 21) return WL_LAYER_MENU;
|
||||
if (level < 50) return WL_LAYER_DOCK;
|
||||
if (level < 100) return WL_LAYER_FULLSCREEN;
|
||||
return WL_LAYER_OVERLAY;
|
||||
}
|
||||
|
||||
/* Return the level_trees[] entry for a given WM level integer.
|
||||
* Falls back to the root scene tree only if the scene is not yet
|
||||
* initialised, which cannot happen during normal window management. */
|
||||
static struct wlr_scene_tree *
|
||||
level_tree_for(int level)
|
||||
{
|
||||
int b = level_to_bucket(level);
|
||||
if (wl_state.level_trees[b])
|
||||
return wl_state.level_trees[b];
|
||||
/* Scene not yet initialised — this window will be invisible. */
|
||||
return &wl_state.scene->tree;
|
||||
}
|
||||
|
||||
static void
|
||||
wl_stacking_set_level(WNativeWindow win, int level)
|
||||
{
|
||||
/* Move the frame's grouping tree into the correct level bucket so
|
||||
* that the scene graph structurally enforces level ordering. All
|
||||
* other nodes (background rect, dock panel, OR views, overlays)
|
||||
* are already placed in their fixed buckets at creation time. */
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (!fb || !fb->tree) return;
|
||||
struct wlr_scene_tree *target = level_tree_for(level);
|
||||
if (fb->tree->node.parent != target)
|
||||
wlr_scene_node_reparent(&fb->tree->node, target);
|
||||
}
|
||||
|
||||
static void
|
||||
wl_stacking_raise(WNativeWindow win)
|
||||
{
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v) {
|
||||
wl_list_remove(&v->link);
|
||||
wl_list_insert(&wl_state.toplevel_list, &v->link);
|
||||
if (v->frame_id) {
|
||||
struct wl_frame_buf *fb = frame_buf_find(v->frame_id);
|
||||
if (fb && fb->tree) {
|
||||
wlr_scene_node_raise_to_top(&fb->tree->node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (v->scene_node)
|
||||
wlr_scene_node_raise_to_top(&v->scene_node->node);
|
||||
return;
|
||||
}
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (fb) {
|
||||
if (fb->tree) {
|
||||
wlr_scene_node_raise_to_top(&fb->tree->node);
|
||||
} else if (fb->scene_buf) {
|
||||
wlr_scene_node_raise_to_top(&fb->scene_buf->node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wl_stacking_lower(WNativeWindow win)
|
||||
{
|
||||
/* Frame-buf path: operate on the grouping tree, not the inner buffer
|
||||
* node. Lowering fb->tree moves the whole window (frame pixels +
|
||||
* reparented client surface) together, within its level bucket. */
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (fb) {
|
||||
if (fb->tree)
|
||||
wlr_scene_node_lower_to_bottom(&fb->tree->node);
|
||||
else if (fb->scene_buf)
|
||||
wlr_scene_node_lower_to_bottom(&fb->scene_buf->node);
|
||||
return;
|
||||
}
|
||||
/* Unframed toplevel view (no frame_buf wrapper). */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v) {
|
||||
wl_list_remove(&v->link);
|
||||
wl_list_insert(wl_state.toplevel_list.prev, &v->link);
|
||||
if (v->scene_node)
|
||||
wlr_scene_node_lower_to_bottom(&v->scene_node->node);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wl_stacking_restack(WNativeWindow *windows, int count)
|
||||
{
|
||||
/* Reorder scene graph to match the WM stacking order.
|
||||
*
|
||||
* windows[] is ordered top-to-bottom (index 0 = topmost).
|
||||
* Process bottom-to-top: last raise_to_top call wins, so the
|
||||
* window at index 0 ends up on top within its level bucket.
|
||||
*
|
||||
* Each window is represented by its toplevel frame_buf ID. We
|
||||
* raise fb->tree — the grouping tree that owns both the frame
|
||||
* pixel buffer and the reparented client surface — so the entire
|
||||
* window moves as one atomic unit. Child frame_bufs (titlebar,
|
||||
* resizebar) are already children of fb->tree and move with it. */
|
||||
int i;
|
||||
for (i = count - 1; i >= 0; i--) {
|
||||
WNativeWindow win = windows[i];
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (fb) {
|
||||
/* Raise the grouping tree: moves frame + client together. */
|
||||
if (fb->tree)
|
||||
wlr_scene_node_raise_to_top(&fb->tree->node);
|
||||
else if (fb->scene_buf)
|
||||
wlr_scene_node_raise_to_top(&fb->scene_buf->node);
|
||||
continue;
|
||||
}
|
||||
/* Fallback: unframed toplevel view (no frame_buf). */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v) {
|
||||
wl_list_remove(&v->link);
|
||||
wl_list_insert(&wl_state.toplevel_list, &v->link);
|
||||
if (v->scene_node)
|
||||
wlr_scene_node_raise_to_top(&v->scene_node->node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
wl_stacking_query(WNativeWindow root,
|
||||
WNativeWindow **out, unsigned int *count)
|
||||
{
|
||||
/* Phase 58: return synthetic IDs in Z-order (top to bottom).
|
||||
* RemakeStackList will find no WM_CTX_STACK entries for them
|
||||
* and set window_count=0, but returning 1 avoids the "could not
|
||||
* get window list" warning. */
|
||||
struct wl_toplevel_view *v;
|
||||
unsigned int n = 0, i = 0;
|
||||
WNativeWindow *wins;
|
||||
(void)root;
|
||||
|
||||
wl_list_for_each(v, &wl_state.toplevel_list, link) n++;
|
||||
if (n == 0) { *out = NULL; *count = 0; return 1; }
|
||||
|
||||
wins = wmalloc(n * sizeof(WNativeWindow));
|
||||
wl_list_for_each(v, &wl_state.toplevel_list, link) wins[i++] = v->id;
|
||||
*out = wins; *count = n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Events */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void wl_event_flush(void)
|
||||
{
|
||||
/* Flush pending Xlib requests to XWayland so drawing becomes visible. */
|
||||
if (wl_state.x_display)
|
||||
XFlush(wl_state.x_display);
|
||||
}
|
||||
static void wl_event_flush_expose(void) {}
|
||||
static void wl_event_sync(int discard)
|
||||
{
|
||||
if (wl_state.x_display)
|
||||
XSync(wl_state.x_display, discard ? True : False);
|
||||
}
|
||||
static int wl_event_pending(void) { return 0; }
|
||||
static void wl_event_drain_motion(void) {}
|
||||
static void wl_event_discard_enter_leave_until(unsigned long timestamp) { (void)timestamp; }
|
||||
static void wl_event_handle_extensions(WMEvent *event) { (void)event; }
|
||||
static void wl_event_randr_update(WMEvent *event) { (void)event; }
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Properties */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -7240,75 +6785,7 @@ static void wl_window_add_events(WNativeWindow win, unsigned long extra_mask) {
|
|||
static void wl_window_select_events(WNativeWindow win, unsigned long mask) { (void)win; (void)mask; }
|
||||
|
||||
static void wl_window_shape_clear(WWindow *wwin) { (void)wwin; }
|
||||
static void wl_window_shape_set(WWindow *wwin) { (void)wwin; }
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Overlays */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static 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();
|
||||
}
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static 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();
|
||||
}
|
||||
|
||||
static 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);
|
||||
}
|
||||
|
||||
static 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]);
|
||||
}
|
||||
|
||||
static int
|
||||
wl_overlay_text_width(WScreen *scr, const char *str, int n)
|
||||
{
|
||||
(void)scr; (void)str;
|
||||
return n * OVL_CW;
|
||||
}
|
||||
void wl_window_shape_set(WWindow *wwin) { (void)wwin; }
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Client */
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,460 @@
|
|||
#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/types/wlr_buffer.h>
|
||||
#include <wlr/interfaces/wlr_buffer.h>
|
||||
#include <wlr/xwayland.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <pixman.h>
|
||||
#include <WINGs/WINGsP.h>
|
||||
#include "../../window.h"
|
||||
#include "wl_types.h"
|
||||
|
||||
(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).
|
||||
* 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;
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,25 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include <X11/Xlib.h>
|
||||
#include "../../screen.h"
|
||||
#include "wl_types.h"
|
||||
|
||||
|
||||
void wl_event_flush(void)
|
||||
{
|
||||
/* Flush pending Xlib requests to XWayland so drawing becomes visible. */
|
||||
if (wl_state.x_display)
|
||||
XFlush(wl_state.x_display);
|
||||
}
|
||||
void wl_event_flush_expose(void) {}
|
||||
void wl_event_sync(int discard)
|
||||
{
|
||||
if (wl_state.x_display)
|
||||
XSync(wl_state.x_display, discard ? True : False);
|
||||
}
|
||||
int wl_event_pending(void) { return 0; }
|
||||
void wl_event_drain_motion(void) {}
|
||||
void wl_event_discard_enter_leave_until(unsigned long timestamp) { (void)timestamp; }
|
||||
void wl_event_handle_extensions(WMEvent *event) { (void)event; }
|
||||
void wl_event_randr_update(WMEvent *event) { (void)event; }
|
||||
|
|
@ -0,0 +1,355 @@
|
|||
#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_output.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <pixman.h>
|
||||
#include "../../screen.h"
|
||||
#include "wl_types.h"
|
||||
#include <wlr/types/wlr_output.h>
|
||||
|
||||
/* Forward declarations for helpers defined in wl_backend.c */
|
||||
struct wm_shm_surface *wm_shm_surface_create(int width, int height);
|
||||
void wm_shm_surface_destroy(struct wm_shm_surface *s);
|
||||
void wm_shm_surface_commit(struct wm_shm_surface *s,
|
||||
struct wlr_scene_tree *parent, int x, int y);
|
||||
void wm_shm_surface_fill(struct wm_shm_surface *s,
|
||||
int x, int y, int w, int h, uint32_t argb);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Phase 81: Overlay animation surface — XOR pixel engine + bitmap */
|
||||
/* font for geometry display (miniaturize zoom, transparent */
|
||||
/* move/resize frames, position/size readouts). */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* XOR mask: 0xFFFFFFFF XOR 0x00000000 (transparent) = 0xFFFFFFFF (opaque
|
||||
* white); XOR 0xFFFFFFFF again = 0x00000000 (transparent, i.e. erased).
|
||||
* The overlay scene buffer is composited OVER all client windows via
|
||||
* standard alpha blending, so transparent pixels show through to the
|
||||
* desktop and opaque-white pixels appear on top.
|
||||
*/
|
||||
#define OVL_MASK 0xFFFFFFFFu
|
||||
|
||||
/*
|
||||
* 5-wide × 9-tall bitmap font for printable ASCII (0x20–0x7E).
|
||||
* Encoding: bit 7 = column 0 (left), bit 3 = column 4 (right), bits 2–0
|
||||
* unused. Cell width = 6 px (glyph 5 + 1 gap); cell height = 10 px.
|
||||
*/
|
||||
#define OVL_GW 5 /* glyph pixel width */
|
||||
#define OVL_GH 9 /* glyph pixel height */
|
||||
#define OVL_CW 6 /* cell width (px) */
|
||||
#define OVL_CH 10 /* cell height (px) */
|
||||
|
||||
const uint8_t ovl_font[96][OVL_GH] = {
|
||||
/*20 ' '*/{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/*21 '!'*/{0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00},
|
||||
/*22 '"'*/{0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/*23 '#'*/{0x50,0x50,0xF8,0x50,0xF8,0x50,0x50,0x00,0x00},
|
||||
/*24 '$'*/{0x20,0x70,0xA0,0x70,0x28,0x70,0x20,0x00,0x00},
|
||||
/*25 '%'*/{0xC0,0xC8,0x10,0x20,0x40,0x98,0x18,0x00,0x00},
|
||||
/*26 '&'*/{0x60,0x90,0x60,0xA0,0x98,0x90,0x68,0x00,0x00},
|
||||
/*27'\''*/ {0x60,0x20,0x40,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/*28 '('*/{0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00,0x00},
|
||||
/*29 ')'*/{0x40,0x20,0x10,0x10,0x10,0x20,0x40,0x00,0x00},
|
||||
/*2A '*'*/{0x00,0x88,0x50,0xF8,0x50,0x88,0x00,0x00,0x00},
|
||||
/*2B '+'*/{0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00,0x00},
|
||||
/*2C ','*/{0x00,0x00,0x00,0x00,0x60,0x20,0x40,0x00,0x00},
|
||||
/*2D '-'*/{0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00},
|
||||
/*2E '.'*/{0x00,0x00,0x00,0x00,0x00,0x60,0x60,0x00,0x00},
|
||||
/*2F '/'*/{0x08,0x08,0x10,0x20,0x40,0x80,0x80,0x00,0x00},
|
||||
/*30 '0'*/{0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00},
|
||||
/*31 '1'*/{0x20,0x60,0x20,0x20,0x20,0x20,0x70,0x00,0x00},
|
||||
/*32 '2'*/{0x70,0x88,0x08,0x30,0x40,0x80,0xF8,0x00,0x00},
|
||||
/*33 '3'*/{0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00,0x00},
|
||||
/*34 '4'*/{0x10,0x30,0x50,0x90,0xF8,0x10,0x10,0x00,0x00},
|
||||
/*35 '5'*/{0xF8,0x80,0xF0,0x08,0x08,0x88,0x70,0x00,0x00},
|
||||
/*36 '6'*/{0x38,0x40,0x80,0xF0,0x88,0x88,0x70,0x00,0x00},
|
||||
/*37 '7'*/{0xF8,0x08,0x10,0x20,0x20,0x20,0x20,0x00,0x00},
|
||||
/*38 '8'*/{0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x00},
|
||||
/*39 '9'*/{0x70,0x88,0x88,0x78,0x08,0x08,0x70,0x00,0x00},
|
||||
/*3A ':'*/{0x00,0x60,0x60,0x00,0x60,0x60,0x00,0x00,0x00},
|
||||
/*3B ';'*/{0x00,0x60,0x60,0x00,0x60,0x20,0x40,0x00,0x00},
|
||||
/*3C '<'*/{0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00,0x00},
|
||||
/*3D '='*/{0x00,0x00,0xF8,0x00,0xF8,0x00,0x00,0x00,0x00},
|
||||
/*3E '>'*/{0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x00},
|
||||
/*3F '?'*/{0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00,0x00},
|
||||
/*40 '@'*/{0x70,0x88,0x98,0xA8,0x98,0x80,0x70,0x00,0x00},
|
||||
/*41 'A'*/{0x20,0x50,0x88,0x88,0xF8,0x88,0x88,0x00,0x00},
|
||||
/*42 'B'*/{0xF0,0x88,0x88,0xF0,0x88,0x88,0xF0,0x00,0x00},
|
||||
/*43 'C'*/{0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00,0x00},
|
||||
/*44 'D'*/{0xE0,0x90,0x88,0x88,0x88,0x90,0xE0,0x00,0x00},
|
||||
/*45 'E'*/{0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8,0x00,0x00},
|
||||
/*46 'F'*/{0xF8,0x80,0x80,0xF0,0x80,0x80,0x80,0x00,0x00},
|
||||
/*47 'G'*/{0x70,0x88,0x80,0xB8,0x88,0x88,0x70,0x00,0x00},
|
||||
/*48 'H'*/{0x88,0x88,0x88,0xF8,0x88,0x88,0x88,0x00,0x00},
|
||||
/*49 'I'*/{0x70,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00},
|
||||
/*4A 'J'*/{0x38,0x10,0x10,0x10,0x10,0x90,0x60,0x00,0x00},
|
||||
/*4B 'K'*/{0x88,0x90,0xA0,0xC0,0xA0,0x90,0x88,0x00,0x00},
|
||||
/*4C 'L'*/{0x80,0x80,0x80,0x80,0x80,0x80,0xF8,0x00,0x00},
|
||||
/*4D 'M'*/{0x88,0xD8,0xA8,0x88,0x88,0x88,0x88,0x00,0x00},
|
||||
/*4E 'N'*/{0x88,0xC8,0xA8,0x98,0x88,0x88,0x88,0x00,0x00},
|
||||
/*4F 'O'*/{0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00},
|
||||
/*50 'P'*/{0xF0,0x88,0x88,0xF0,0x80,0x80,0x80,0x00,0x00},
|
||||
/*51 'Q'*/{0x70,0x88,0x88,0x88,0xA8,0x90,0x68,0x00,0x00},
|
||||
/*52 'R'*/{0xF0,0x88,0x88,0xF0,0xA0,0x90,0x88,0x00,0x00},
|
||||
/*53 'S'*/{0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00,0x00},
|
||||
/*54 'T'*/{0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x00},
|
||||
/*55 'U'*/{0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00},
|
||||
/*56 'V'*/{0x88,0x88,0x88,0x88,0x50,0x50,0x20,0x00,0x00},
|
||||
/*57 'W'*/{0x88,0x88,0x88,0xA8,0xA8,0xD8,0x88,0x00,0x00},
|
||||
/*58 'X'*/{0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x00},
|
||||
/*59 'Y'*/{0x88,0x88,0x50,0x20,0x20,0x20,0x20,0x00,0x00},
|
||||
/*5A 'Z'*/{0xF8,0x08,0x10,0x20,0x40,0x80,0xF8,0x00,0x00},
|
||||
/*5B '['*/{0x70,0x40,0x40,0x40,0x40,0x40,0x70,0x00,0x00},
|
||||
/*5C'\\'*/ {0x80,0x80,0x40,0x20,0x10,0x08,0x08,0x00,0x00},
|
||||
/*5D ']'*/{0x70,0x10,0x10,0x10,0x10,0x10,0x70,0x00,0x00},
|
||||
/*5E '^'*/{0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/*5F '_'*/{0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x00,0x00},
|
||||
/*60 '`'*/{0x40,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
/*61 'a'*/{0x00,0x00,0x70,0x08,0x78,0x88,0x78,0x00,0x00},
|
||||
/*62 'b'*/{0x80,0x80,0xF0,0x88,0x88,0x88,0xF0,0x00,0x00},
|
||||
/*63 'c'*/{0x00,0x00,0x70,0x88,0x80,0x88,0x70,0x00,0x00},
|
||||
/*64 'd'*/{0x08,0x08,0x78,0x88,0x88,0x88,0x78,0x00,0x00},
|
||||
/*65 'e'*/{0x00,0x00,0x70,0x88,0xF8,0x80,0x70,0x00,0x00},
|
||||
/*66 'f'*/{0x30,0x48,0x40,0xE0,0x40,0x40,0x40,0x00,0x00},
|
||||
/*67 'g'*/{0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x70,0x00},
|
||||
/*68 'h'*/{0x80,0x80,0xF0,0x88,0x88,0x88,0x88,0x00,0x00},
|
||||
/*69 'i'*/{0x20,0x00,0x60,0x20,0x20,0x20,0x70,0x00,0x00},
|
||||
/*6A 'j'*/{0x10,0x00,0x30,0x10,0x10,0x10,0x90,0x60,0x00},
|
||||
/*6B 'k'*/{0x80,0x80,0x90,0xA0,0xC0,0xA0,0x90,0x00,0x00},
|
||||
/*6C 'l'*/{0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00},
|
||||
/*6D 'm'*/{0x00,0x00,0xD8,0xA8,0xA8,0xA8,0x88,0x00,0x00},
|
||||
/*6E 'n'*/{0x00,0x00,0xF0,0x88,0x88,0x88,0x88,0x00,0x00},
|
||||
/*6F 'o'*/{0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00,0x00},
|
||||
/*70 'p'*/{0x00,0x00,0xF0,0x88,0x88,0xF0,0x80,0x80,0x00},
|
||||
/*71 'q'*/{0x00,0x00,0x78,0x88,0x88,0x78,0x08,0x08,0x00},
|
||||
/*72 'r'*/{0x00,0x00,0xB0,0xC8,0x80,0x80,0x80,0x00,0x00},
|
||||
/*73 's'*/{0x00,0x00,0x70,0x80,0x70,0x08,0xF0,0x00,0x00},
|
||||
/*74 't'*/{0x40,0x40,0xF0,0x40,0x40,0x48,0x30,0x00,0x00},
|
||||
/*75 'u'*/{0x00,0x00,0x88,0x88,0x88,0x98,0x68,0x00,0x00},
|
||||
/*76 'v'*/{0x00,0x00,0x88,0x88,0x88,0x50,0x20,0x00,0x00},
|
||||
/*77 'w'*/{0x00,0x00,0x88,0x88,0xA8,0xD8,0x88,0x00,0x00},
|
||||
/*78 'x'*/{0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00},
|
||||
/*79 'y'*/{0x00,0x00,0x88,0x88,0x78,0x08,0x70,0x00,0x00},
|
||||
/*7A 'z'*/{0x00,0x00,0xF8,0x10,0x20,0x40,0xF8,0x00,0x00},
|
||||
/*7B '{'*/{0x18,0x20,0x20,0x40,0x20,0x20,0x18,0x00,0x00},
|
||||
/*7C '|'*/{0x20,0x20,0x20,0x00,0x20,0x20,0x20,0x00,0x00},
|
||||
/*7D '}'*/{0x60,0x10,0x10,0x08,0x10,0x10,0x60,0x00,0x00},
|
||||
/*7E '~'*/{0x40,0xA8,0x18,0x00,0x00,0x00,0x00,0x00,0x00},
|
||||
};
|
||||
|
||||
/* Lazily allocate (or resize) the full-screen overlay buffer. */
|
||||
void
|
||||
overlay_ensure(WScreen *scr)
|
||||
{
|
||||
int w, h;
|
||||
if (scr && scr->scr_width > 0 && scr->scr_height > 0) {
|
||||
w = scr->scr_width;
|
||||
h = scr->scr_height;
|
||||
} else if (wl_state.output) {
|
||||
w = (int)wl_state.output->width;
|
||||
h = (int)wl_state.output->height;
|
||||
} else {
|
||||
w = 1920; h = 1080;
|
||||
}
|
||||
if (wl_state.overlay_surf &&
|
||||
wl_state.overlay_surf->width == w &&
|
||||
wl_state.overlay_surf->height == h)
|
||||
return;
|
||||
if (wl_state.overlay_surf) {
|
||||
wm_shm_surface_destroy(wl_state.overlay_surf);
|
||||
wl_state.overlay_surf = NULL;
|
||||
}
|
||||
wl_state.overlay_surf = wm_shm_surface_create(w, h);
|
||||
}
|
||||
|
||||
/* XOR a single pixel on the overlay buffer (clipped). */
|
||||
static inline void
|
||||
ovl_plot(struct wm_shm_surface *s, int x, int y)
|
||||
{
|
||||
if ((unsigned)x < (unsigned)s->width && (unsigned)y < (unsigned)s->height)
|
||||
s->data[y * (int)(s->stride >> 2) + x] ^= OVL_MASK;
|
||||
}
|
||||
|
||||
/* XOR a horizontal run of `len` pixels starting at (x, y). */
|
||||
void
|
||||
ovl_hline(struct wm_shm_surface *s, int x, int y, int len)
|
||||
{
|
||||
if ((unsigned)y >= (unsigned)s->height || len <= 0) return;
|
||||
int x0 = x < 0 ? 0 : x;
|
||||
int x1 = x + len; if (x1 > s->width) x1 = s->width;
|
||||
if (x0 >= x1) return;
|
||||
uint32_t *p = s->data + y * (int)(s->stride >> 2) + x0;
|
||||
for (int i = x0; i < x1; i++) *p++ ^= OVL_MASK;
|
||||
}
|
||||
|
||||
/* XOR a vertical run of `len` pixels starting at (x, y). */
|
||||
void
|
||||
ovl_vline(struct wm_shm_surface *s, int x, int y, int len)
|
||||
{
|
||||
if ((unsigned)x >= (unsigned)s->width || len <= 0) return;
|
||||
int y0 = y < 0 ? 0 : y;
|
||||
int y1 = y + len; if (y1 > s->height) y1 = s->height;
|
||||
int stride = (int)(s->stride >> 2);
|
||||
for (int j = y0; j < y1; j++)
|
||||
s->data[j * stride + x] ^= OVL_MASK;
|
||||
}
|
||||
|
||||
/* Bresenham line (with fast h/v paths). */
|
||||
void
|
||||
ovl_line(struct wm_shm_surface *s, int x0, int y0, int x1, int y1)
|
||||
{
|
||||
if (x0 == x1) {
|
||||
int y = y0 < y1 ? y0 : y1;
|
||||
ovl_vline(s, x0, y, abs(y1 - y0) + 1);
|
||||
return;
|
||||
}
|
||||
if (y0 == y1) {
|
||||
int x = x0 < x1 ? x0 : x1;
|
||||
ovl_hline(s, x, y0, abs(x1 - x0) + 1);
|
||||
return;
|
||||
}
|
||||
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||
int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx + dy;
|
||||
for (;;) {
|
||||
ovl_plot(s, x0, y0);
|
||||
if (x0 == x1 && y0 == y1) break;
|
||||
int e2 = 2 * err;
|
||||
if (e2 >= dy) { err += dy; x0 += sx; }
|
||||
if (e2 <= dx) { err += dx; y0 += sy; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Render one glyph at cell origin (cx, cy) using XOR. */
|
||||
void
|
||||
ovl_draw_glyph(struct wm_shm_surface *s, int cx, int cy, unsigned char ch)
|
||||
{
|
||||
if (ch < 0x20 || ch > 0x7E) ch = '?';
|
||||
const uint8_t *g = ovl_font[ch - 0x20];
|
||||
for (int row = 0; row < OVL_GH; row++) {
|
||||
uint8_t bits = g[row];
|
||||
if (!bits) continue;
|
||||
int py = cy + row;
|
||||
if (py < 0 || py >= s->height) continue;
|
||||
for (int col = 0; col < OVL_GW; col++) {
|
||||
if (bits & (0x80u >> col))
|
||||
ovl_plot(s, cx + col, py);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Push the modified overlay buffer to the scene and render a frame. */
|
||||
void
|
||||
overlay_flush(void)
|
||||
{
|
||||
struct wm_shm_surface *s = wl_state.overlay_surf;
|
||||
if (!s || !wl_state.scene) return;
|
||||
/* First call creates the scene_buffer node; subsequent calls reposition. */
|
||||
/* Overlay surface lives in WL_LAYER_OVERLAY so it is structurally
|
||||
* above every window and dock node without needing raise_to_top. */
|
||||
{
|
||||
struct wlr_scene_tree *_op = wl_state.level_trees[WL_LAYER_OVERLAY]
|
||||
? wl_state.level_trees[WL_LAYER_OVERLAY] : &wl_state.scene->tree;
|
||||
wm_shm_surface_commit(s, _op, 0, 0);
|
||||
}
|
||||
if (s->scene_buf) {
|
||||
/* Ensure the node is visible for the animation frame. */
|
||||
wlr_scene_node_set_enabled(&s->scene_buf->node, true);
|
||||
/* Notify wlroots that the pixel data has changed. */
|
||||
wlr_scene_buffer_set_buffer_with_damage(s->scene_buf, &s->wlr_buf, NULL);
|
||||
}
|
||||
/* Commit the scene immediately so the animation step is visible
|
||||
* before the caller's wusleep() delay. */
|
||||
if (wl_state.scene_output && wl_state.allocator)
|
||||
wlr_scene_output_commit(wl_state.scene_output, NULL);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Server grab */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
void wl_server_grab(void) {} /* no-op: Wayland double-buffers for us */
|
||||
void wl_server_ungrab(void)
|
||||
{
|
||||
overlay_flush();
|
||||
/* Disable the overlay scene node so it doesn't intercept hit-testing.
|
||||
* The XOR animation has already erased its pixels; leaving the node
|
||||
* enabled blocks frame_buf_at (wlr_scene_node_at hits the overlay
|
||||
* before any frame_buf). */
|
||||
if (wl_state.overlay_surf && wl_state.overlay_surf->scene_buf)
|
||||
wlr_scene_node_set_enabled(&wl_state.overlay_surf->scene_buf->node, false);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,377 @@
|
|||
#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).
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
#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 "../../screen.h"
|
||||
#include "wl_types.h"
|
||||
|
||||
struct wl_frame_buf *frame_buf_find(WNativeWindow id);
|
||||
struct wl_toplevel_view *wl_find_view_by_id(WNativeWindow id);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Stacking */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* Map a WM window level integer to a level_trees[] bucket index.
|
||||
* Buckets are ordered 0=lowest (WL_LAYER_BACKGROUND) to 8=highest
|
||||
* (WL_LAYER_OVERLAY), matching the bottom-to-top creation order of
|
||||
* level_trees[]. Does not map WL_LAYER_BACKGROUND (no WM level). */
|
||||
int
|
||||
level_to_bucket(int level)
|
||||
{
|
||||
if (level < -1) return WL_LAYER_DESKTOP;
|
||||
if (level < 0) return WL_LAYER_SUNKEN;
|
||||
if (level < 3) return WL_LAYER_NORMAL;
|
||||
if (level < 20) return WL_LAYER_FLOATING;
|
||||
if (level < 21) return WL_LAYER_MENU;
|
||||
if (level < 50) return WL_LAYER_DOCK;
|
||||
if (level < 100) return WL_LAYER_FULLSCREEN;
|
||||
return WL_LAYER_OVERLAY;
|
||||
}
|
||||
|
||||
/* Return the level_trees[] entry for a given WM level integer.
|
||||
* Falls back to the root scene tree only if the scene is not yet
|
||||
* initialised, which cannot happen during normal window management. */
|
||||
struct wlr_scene_tree *
|
||||
level_tree_for(int level)
|
||||
{
|
||||
int b = level_to_bucket(level);
|
||||
if (wl_state.level_trees[b])
|
||||
return wl_state.level_trees[b];
|
||||
/* Scene not yet initialised — this window will be invisible. */
|
||||
return &wl_state.scene->tree;
|
||||
}
|
||||
|
||||
void
|
||||
wl_stacking_set_level(WNativeWindow win, int level)
|
||||
{
|
||||
/* Move the frame's grouping tree into the correct level bucket so
|
||||
* that the scene graph structurally enforces level ordering. All
|
||||
* other nodes (background rect, dock panel, OR views, overlays)
|
||||
* are already placed in their fixed buckets at creation time. */
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (!fb || !fb->tree) return;
|
||||
struct wlr_scene_tree *target = level_tree_for(level);
|
||||
if (fb->tree->node.parent != target)
|
||||
wlr_scene_node_reparent(&fb->tree->node, target);
|
||||
}
|
||||
|
||||
void
|
||||
wl_stacking_raise(WNativeWindow win)
|
||||
{
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v) {
|
||||
wl_list_remove(&v->link);
|
||||
wl_list_insert(&wl_state.toplevel_list, &v->link);
|
||||
if (v->frame_id) {
|
||||
struct wl_frame_buf *fb = frame_buf_find(v->frame_id);
|
||||
if (fb && fb->tree) {
|
||||
wlr_scene_node_raise_to_top(&fb->tree->node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (v->scene_node)
|
||||
wlr_scene_node_raise_to_top(&v->scene_node->node);
|
||||
return;
|
||||
}
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (fb) {
|
||||
if (fb->tree) {
|
||||
wlr_scene_node_raise_to_top(&fb->tree->node);
|
||||
} else if (fb->scene_buf) {
|
||||
wlr_scene_node_raise_to_top(&fb->scene_buf->node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
wl_stacking_lower(WNativeWindow win)
|
||||
{
|
||||
/* Frame-buf path: operate on the grouping tree, not the inner buffer
|
||||
* node. Lowering fb->tree moves the whole window (frame pixels +
|
||||
* reparented client surface) together, within its level bucket. */
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (fb) {
|
||||
if (fb->tree)
|
||||
wlr_scene_node_lower_to_bottom(&fb->tree->node);
|
||||
else if (fb->scene_buf)
|
||||
wlr_scene_node_lower_to_bottom(&fb->scene_buf->node);
|
||||
return;
|
||||
}
|
||||
/* Unframed toplevel view (no frame_buf wrapper). */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v) {
|
||||
wl_list_remove(&v->link);
|
||||
wl_list_insert(wl_state.toplevel_list.prev, &v->link);
|
||||
if (v->scene_node)
|
||||
wlr_scene_node_lower_to_bottom(&v->scene_node->node);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
wl_stacking_restack(WNativeWindow *windows, int count)
|
||||
{
|
||||
/* Reorder scene graph to match the WM stacking order.
|
||||
*
|
||||
* windows[] is ordered top-to-bottom (index 0 = topmost).
|
||||
* Process bottom-to-top: last raise_to_top call wins, so the
|
||||
* window at index 0 ends up on top within its level bucket.
|
||||
*
|
||||
* Each window is represented by its toplevel frame_buf ID. We
|
||||
* raise fb->tree — the grouping tree that owns both the frame
|
||||
* pixel buffer and the reparented client surface — so the entire
|
||||
* window moves as one atomic unit. Child frame_bufs (titlebar,
|
||||
* resizebar) are already children of fb->tree and move with it. */
|
||||
int i;
|
||||
for (i = count - 1; i >= 0; i--) {
|
||||
WNativeWindow win = windows[i];
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (fb) {
|
||||
/* Raise the grouping tree: moves frame + client together. */
|
||||
if (fb->tree)
|
||||
wlr_scene_node_raise_to_top(&fb->tree->node);
|
||||
else if (fb->scene_buf)
|
||||
wlr_scene_node_raise_to_top(&fb->scene_buf->node);
|
||||
continue;
|
||||
}
|
||||
/* Fallback: unframed toplevel view (no frame_buf). */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v) {
|
||||
wl_list_remove(&v->link);
|
||||
wl_list_insert(&wl_state.toplevel_list, &v->link);
|
||||
if (v->scene_node)
|
||||
wlr_scene_node_raise_to_top(&v->scene_node->node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
wl_stacking_query(WNativeWindow root,
|
||||
WNativeWindow **out, unsigned int *count)
|
||||
{
|
||||
/* Phase 58: return synthetic IDs in Z-order (top to bottom).
|
||||
* RemakeStackList will find no WM_CTX_STACK entries for them
|
||||
* and set window_count=0, but returning 1 avoids the "could not
|
||||
* get window list" warning. */
|
||||
struct wl_toplevel_view *v;
|
||||
unsigned int n = 0, i = 0;
|
||||
WNativeWindow *wins;
|
||||
(void)root;
|
||||
|
||||
wl_list_for_each(v, &wl_state.toplevel_list, link) n++;
|
||||
if (n == 0) { *out = NULL; *count = 0; return 1; }
|
||||
|
||||
wins = wmalloc(n * sizeof(WNativeWindow));
|
||||
wl_list_for_each(v, &wl_state.toplevel_list, link) wins[i++] = v->id;
|
||||
*out = wins; *count = n;
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -7,6 +7,17 @@
|
|||
#include <wlr/types/wlr_buffer.h> /* Phase 29+: WM-owned SHM surface buffers */
|
||||
#include "wl_backend.h"
|
||||
|
||||
/* SHM surface — pixman-backed wlr_buffer for WM-owned rendering */
|
||||
struct wm_shm_surface {
|
||||
struct wlr_buffer wlr_buf;
|
||||
uint32_t *data;
|
||||
int width;
|
||||
int height;
|
||||
size_t stride;
|
||||
pixman_image_t *image;
|
||||
struct wlr_scene_buffer *scene_buf;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Phase 38: per-toplevel view and context store */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -380,3 +391,35 @@ void * wl_context_find(WNativeWindow win, WContextKey key);
|
|||
int wl_context_delete(WNativeWindow win, WContextKey key);
|
||||
|
||||
#endif /* WL_TYPES_H */
|
||||
|
||||
/* wl_event.c */
|
||||
void wl_event_flush(void);
|
||||
void wl_event_flush_expose(void);
|
||||
void wl_event_sync(int discard);
|
||||
int wl_event_pending(void);
|
||||
void wl_event_drain_motion(void);
|
||||
void wl_event_discard_enter_leave_until(unsigned long timestamp);
|
||||
void wl_event_handle_extensions(WMEvent *event);
|
||||
void wl_event_randr_update(WMEvent *event);
|
||||
|
||||
void wl_stacking_set_level(WNativeWindow win, int level);
|
||||
void wl_stacking_raise(WNativeWindow win);
|
||||
void wl_stacking_lower(WNativeWindow win);
|
||||
void wl_stacking_restack(WNativeWindow *windows, int count);
|
||||
int wl_stacking_query(WNativeWindow root, WNativeWindow **out, unsigned int *count);
|
||||
|
||||
void wl_server_grab(void);
|
||||
void wl_server_ungrab(void);
|
||||
void wl_overlay_draw_line(WScreen *scr, int x1, int y1, int x2, int y2);
|
||||
void wl_overlay_draw_lines(WScreen *scr, WPoint *pts, int n);
|
||||
void wl_overlay_draw_rect(WScreen *scr, int x, int y, int w, int h);
|
||||
void wl_overlay_draw_segments(WScreen *scr, WSegment *segs, int n);
|
||||
void wl_overlay_draw_string(WScreen *scr, int x, int y, const char *str, int len);
|
||||
int wl_overlay_text_width(WScreen *scr, const char *str, int n);
|
||||
|
||||
/* Overlay font metrics */
|
||||
#define OVL_GH 9
|
||||
#define OVL_CW 6
|
||||
|
||||
void wl_window_shape_set(WWindow *wwin);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,598 @@
|
|||
#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/types/wlr_buffer.h>
|
||||
#include <wlr/interfaces/wlr_buffer.h>
|
||||
#include <wlr/xwayland.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <pixman.h>
|
||||
#include <WINGs/WINGsP.h>
|
||||
#include "../../window.h"
|
||||
#include "../../framewin.h"
|
||||
#include "wl_types.h"
|
||||
|
||||
|
||||
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 */
|
||||
|
||||
void
|
||||
wl_window_map(WNativeWindow win)
|
||||
{
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v && v->scene_node) {
|
||||
wlr_scene_node_set_enabled(&v->scene_node->node, true);
|
||||
return;
|
||||
}
|
||||
if (v)
|
||||
return;
|
||||
/* Frame buffer: enable its scene node and children. */
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (fb && fb->scene_buf) {
|
||||
/* If this is a child, only enable if parent is enabled */
|
||||
if (fb->parent_id) {
|
||||
struct wl_frame_buf *parent = frame_buf_find(fb->parent_id);
|
||||
if (parent && parent->scene_buf &&
|
||||
!parent->scene_buf->node.enabled)
|
||||
return; /* parent not visible yet */
|
||||
}
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, true);
|
||||
/* Raise the tree (raises entire window assembly together) */
|
||||
if (fb->tree) {
|
||||
wlr_scene_node_set_enabled(&fb->tree->node, true);
|
||||
wlr_scene_node_raise_to_top(&fb->tree->node);
|
||||
} else if (!fb->parent_id)
|
||||
wlr_scene_node_raise_to_top(&fb->scene_buf->node);
|
||||
/* Also enable children (and grandchildren) */
|
||||
struct wl_frame_buf *child;
|
||||
wl_list_for_each(child, &wl_state.frame_list, link) {
|
||||
if (child->parent_id == win && child->scene_buf) {
|
||||
wlr_scene_node_set_enabled(&child->scene_buf->node, true);
|
||||
struct wl_frame_buf *gc;
|
||||
wl_list_for_each(gc, &wl_state.frame_list, link) {
|
||||
if (gc->parent_id == child->id && gc->scene_buf)
|
||||
wlr_scene_node_set_enabled(&gc->scene_buf->node, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
wl_window_map_raised(WNativeWindow win)
|
||||
{
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v && v->scene_node) {
|
||||
wlr_scene_node_set_enabled(&v->scene_node->node, true);
|
||||
if (v->frame_id) {
|
||||
struct wl_frame_buf *fb = frame_buf_find(v->frame_id);
|
||||
if (fb && fb->tree) {
|
||||
wlr_scene_node_raise_to_top(&fb->tree->node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
wlr_scene_node_raise_to_top(&v->scene_node->node);
|
||||
return;
|
||||
}
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (fb && fb->scene_buf) {
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, true);
|
||||
if (fb->tree) {
|
||||
/* Top-level frame: enable tree (decoration children become visible). */
|
||||
wlr_scene_node_set_enabled(&fb->tree->node, true);
|
||||
wlr_scene_node_raise_to_top(&fb->tree->node);
|
||||
} else {
|
||||
wlr_scene_node_raise_to_top(&fb->scene_buf->node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
void wl_window_map_subwindows(WNativeWindow parent)
|
||||
{
|
||||
(void)parent;
|
||||
/* Child windows are sub-regions of the frame buffer — no-op. */
|
||||
}
|
||||
|
||||
void
|
||||
wl_window_unmap(WNativeWindow win)
|
||||
{
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v && v->scene_node) {
|
||||
wlr_scene_node_set_enabled(&v->scene_node->node, false);
|
||||
return;
|
||||
}
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (!fb) return;
|
||||
/* For top-level frames, disable the whole tree. This hides the frame
|
||||
* buffer, all child scene_bufs (titlebar, resizebar, buttons), and the
|
||||
* client surface reparented into fb->tree — atomically and irrevocably
|
||||
* until the frame is explicitly mapped again. Disabling individual
|
||||
* child nodes is insufficient because a subsequent wl_frame_paint can
|
||||
* re-enable them while the window is still iconified. */
|
||||
if (fb->tree) {
|
||||
wlr_scene_node_set_enabled(&fb->tree->node, false);
|
||||
return;
|
||||
}
|
||||
/* Child frame_buf: disable its scene_buf node only. */
|
||||
if (fb->scene_buf)
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
void wl_window_move(WNativeWindow win, int x, int y)
|
||||
{
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v) {
|
||||
if (v->frame_id) {
|
||||
/* Client is a child of a frame — coords are relative to frame */
|
||||
v->frame_rel_x = x;
|
||||
v->frame_rel_y = y;
|
||||
struct wl_frame_buf *fb = frame_buf_find(v->frame_id);
|
||||
if (fb) {
|
||||
v->x = fb->x + x;
|
||||
v->y = fb->y + y;
|
||||
}
|
||||
} else {
|
||||
v->x = x; v->y = y;
|
||||
}
|
||||
if (v->scene_node) {
|
||||
/* Scene node is a child of the frame tree — use relative coords */
|
||||
if (v->frame_id)
|
||||
wlr_scene_node_set_position(&v->scene_node->node, x, y);
|
||||
else
|
||||
wlr_scene_node_set_position(&v->scene_node->node, v->x, v->y);
|
||||
}
|
||||
if (v->xw_surface)
|
||||
wlr_xwayland_surface_configure(v->xw_surface,
|
||||
(int16_t)v->x, (int16_t)v->y,
|
||||
v->xw_surface->width, v->xw_surface->height);
|
||||
/* Re-apply any popups whose parent is this toplevel so they
|
||||
* track the parent's position. */
|
||||
struct wl_toplevel_view *p;
|
||||
wl_list_for_each(p, &wl_state.toplevel_list, link) {
|
||||
if (p->popup && p->popup_parent
|
||||
&& wl_find_view_by_xdg_surface(p->popup_parent->base) == v) {
|
||||
wmaker_popup_apply(p);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (fb) {
|
||||
fb->rel_x = x; fb->rel_y = y;
|
||||
if (fb->parent_id) {
|
||||
struct wl_frame_buf *parent = frame_buf_find(fb->parent_id);
|
||||
if (parent) {
|
||||
fb->x = parent->x + x;
|
||||
fb->y = parent->y + y;
|
||||
} else {
|
||||
fb->x = x; fb->y = y;
|
||||
}
|
||||
/* Child frame_buf: reposition scene_buf at relative offset */
|
||||
if (fb->scene_buf)
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, x, y);
|
||||
} else {
|
||||
fb->x = x; fb->y = y;
|
||||
/* Top-level frame: move the tree (children follow automatically) */
|
||||
if (fb->tree)
|
||||
wlr_scene_node_set_position(&fb->tree->node, x, y);
|
||||
}
|
||||
/* Update absolute position tracking for hit-testing — recursively
|
||||
* for all descendants so grandchildren (e.g. WINGs panel inside
|
||||
* a fake_leader inside a WMaker frame) stay in sync. */
|
||||
frame_buf_update_children_abs(win);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void wl_window_resize(WNativeWindow win, int w, int h)
|
||||
{
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v) {
|
||||
if (w <= 0 || h <= 0) return;
|
||||
if (v->xw_surface) {
|
||||
wlr_xwayland_surface_configure(v->xw_surface,
|
||||
(int16_t)v->x, (int16_t)v->y, (uint16_t)w, (uint16_t)h);
|
||||
return;
|
||||
}
|
||||
if (!v->xdg_surface || !v->xdg_surface->toplevel) return;
|
||||
wlr_xdg_toplevel_set_size(v->xdg_surface->toplevel, (uint32_t)w, (uint32_t)h);
|
||||
wlr_xdg_surface_schedule_configure(v->xdg_surface);
|
||||
return;
|
||||
}
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (fb && w > 0 && h > 0 && (w != fb->width || h != fb->height)) {
|
||||
pixman_image_unref(fb->image);
|
||||
fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h, NULL, 0);
|
||||
fb->width = w;
|
||||
fb->height = h;
|
||||
fb->base.width = w;
|
||||
fb->base.height = h;
|
||||
/* Update scene graph's cached buffer size for hit-testing. */
|
||||
if (fb->scene_buf)
|
||||
wlr_scene_buffer_set_dest_size(fb->scene_buf, w, h);
|
||||
}
|
||||
}
|
||||
void wl_window_set_border(WNativeWindow win, unsigned long pixel)
|
||||
{
|
||||
(void)win; (void)pixel; /* borders are drawn into the frame buffer */
|
||||
}
|
||||
void wl_window_set_border_width(WNativeWindow win, int width)
|
||||
{
|
||||
(void)win; (void)width; /* no-op for native frames */
|
||||
}
|
||||
void wl_window_set_save_under(WNativeWindow win, int save_under) { (void)win; (void)save_under; }
|
||||
void wl_noop_window_set_background(WNativeWindow win, unsigned long pixel) { (void)win; (void)pixel; }
|
||||
extern void *W_GetViewBacking(unsigned long id);
|
||||
|
||||
void wl_window_set_background_pixmap(WNativeWindow win, unsigned long pixmap)
|
||||
{
|
||||
if (pixmap == 0) return;
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
pixman_image_t *dst_img = NULL;
|
||||
|
||||
if (fb && fb->image) {
|
||||
dst_img = fb->image;
|
||||
fb->bg_pixmap = (pixman_image_t *)(uintptr_t)pixmap;
|
||||
} else {
|
||||
dst_img = (pixman_image_t *)W_GetViewBacking(win);
|
||||
}
|
||||
if (!dst_img) return;
|
||||
|
||||
pixman_image_t *src = (pixman_image_t *)(uintptr_t)pixmap;
|
||||
int src_w = pixman_image_get_width(src);
|
||||
int src_h = pixman_image_get_height(src);
|
||||
|
||||
/* Blit source pixmap into frame_buf's image */
|
||||
pixman_image_composite32(PIXMAN_OP_SRC,
|
||||
src, /* src */
|
||||
NULL, /* mask */
|
||||
dst_img, /* dest */
|
||||
0, 0, /* src x,y */
|
||||
0, 0, /* mask x,y */
|
||||
0, 0, /* dest x,y */
|
||||
src_w < pixman_image_get_width(dst_img) ? src_w : pixman_image_get_width(dst_img),
|
||||
src_h < pixman_image_get_height(dst_img) ? src_h : pixman_image_get_height(dst_img));
|
||||
if (fb)
|
||||
wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL);
|
||||
}
|
||||
|
||||
void wl_pixmap_free(unsigned long pixmap)
|
||||
{
|
||||
if (pixmap == 0) return;
|
||||
pixman_image_t *img = (pixman_image_t *)(uintptr_t)pixmap;
|
||||
pixman_image_unref(img);
|
||||
}
|
||||
void wl_window_clear(WNativeWindow win)
|
||||
{
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
pixman_image_t *img = NULL;
|
||||
|
||||
if (fb && fb->image) {
|
||||
img = fb->image;
|
||||
} else {
|
||||
img = (pixman_image_t *)W_GetViewBacking(win);
|
||||
}
|
||||
if (!img) return;
|
||||
|
||||
int width = pixman_image_get_width(img);
|
||||
int height = pixman_image_get_height(img);
|
||||
|
||||
/* If a background pixmap is set, repaint from it (like XClearWindow) */
|
||||
if (fb && fb->bg_pixmap) {
|
||||
pixman_image_composite32(PIXMAN_OP_SRC,
|
||||
fb->bg_pixmap, NULL, img,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
width, height);
|
||||
} else {
|
||||
/* No background pixmap — fill with transparent black */
|
||||
int stride = pixman_image_get_stride(img);
|
||||
uint32_t *data = (uint32_t *)pixman_image_get_data(img);
|
||||
memset(data, 0, stride * height);
|
||||
}
|
||||
|
||||
/* Notify the scene buffer of the update */
|
||||
if (fb)
|
||||
wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
wl_window_get_rect(WNativeWindow win,
|
||||
int *win_x, int *win_y, int *win_w, int *win_h)
|
||||
{
|
||||
/* Phase 55: return real geometry for synthetic toplevel IDs. */
|
||||
if (win == WNATIVE_NONE) {
|
||||
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; /* invalid/none */
|
||||
}
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
/* Phase 17: XWayland geometry. */
|
||||
if (v && v->xw_surface) {
|
||||
if (win_x) { *win_x = v->x; }
|
||||
if (win_y) { *win_y = v->y; }
|
||||
if (win_w) { *win_w = v->xw_surface->width > 0 ? v->xw_surface->width : 1; }
|
||||
if (win_h) { *win_h = v->xw_surface->height > 0 ? v->xw_surface->height : 1; }
|
||||
return 1;
|
||||
}
|
||||
if (v && v->xdg_surface && v->xdg_surface->surface) {
|
||||
int w = v->xdg_surface->surface->current.width;
|
||||
int h = v->xdg_surface->surface->current.height;
|
||||
if (win_x) { *win_x = v->x; }
|
||||
if (win_y) { *win_y = v->y; }
|
||||
if (win_w) { *win_w = w > 0 ? w : 1; }
|
||||
if (win_h) { *win_h = h > 0 ? h : 1; }
|
||||
return 1;
|
||||
}
|
||||
/* Frame buffer geometry. */
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
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)
|
||||
{
|
||||
Loading…
Reference in New Issue