decompose wl_backend.c: extract wl_framebuf.c
This commit is contained in:
parent
861ae10c8f
commit
e47b44ccd0
|
|
@ -165,7 +165,8 @@ wmaker_SOURCES += \
|
|||
backend/wayland/wl_window.c \
|
||||
backend/wayland/wl_props.c \
|
||||
backend/wayland/wl_client.c \
|
||||
backend/wayland/wl_icccm.c
|
||||
backend/wayland/wl_icccm.c \
|
||||
backend/wayland/wl_framebuf.c
|
||||
endif
|
||||
|
||||
if USE_NESTED_FUNC
|
||||
|
|
|
|||
|
|
@ -3976,710 +3976,6 @@ wl_show_crash_dialog(int sig)
|
|||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Frame buffer (pixman + wlr_scene_buffer) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void frame_buf_destroy(struct wlr_buffer *wlr_buf)
|
||||
{
|
||||
struct wl_frame_buf *fb = wl_container_of(wlr_buf, fb, base);
|
||||
if (fb->image)
|
||||
pixman_image_unref(fb->image);
|
||||
wfree(fb);
|
||||
}
|
||||
|
||||
static bool frame_buf_begin_data_ptr_access(struct wlr_buffer *wlr_buf,
|
||||
uint32_t flags, void **data, uint32_t *format, size_t *stride)
|
||||
{
|
||||
struct wl_frame_buf *fb = wl_container_of(wlr_buf, fb, base);
|
||||
(void)flags;
|
||||
*data = pixman_image_get_data(fb->image);
|
||||
*format = DRM_FORMAT_ARGB8888;
|
||||
*stride = (size_t)pixman_image_get_stride(fb->image);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void frame_buf_end_data_ptr_access(struct wlr_buffer *wlr_buf)
|
||||
{
|
||||
(void)wlr_buf;
|
||||
}
|
||||
|
||||
const struct wlr_buffer_impl frame_buf_impl = {
|
||||
.destroy = frame_buf_destroy,
|
||||
.begin_data_ptr_access = frame_buf_begin_data_ptr_access,
|
||||
.end_data_ptr_access = frame_buf_end_data_ptr_access,
|
||||
};
|
||||
|
||||
struct wl_frame_buf *
|
||||
frame_buf_find(WNativeWindow id)
|
||||
{
|
||||
struct wl_frame_buf *fb;
|
||||
wl_list_for_each(fb, &wl_state.frame_list, link) {
|
||||
if (fb->id == id) return fb;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* fb_scene_buf_attach / fb_tree_attach
|
||||
*
|
||||
* Register destroy listeners on a scene_buf or tree so that fb->scene_buf
|
||||
* (or fb->tree) is NULL'd when wlroots auto-destroys the node — typically
|
||||
* via cascade when an ancestor scene_tree is destroyed. Without this,
|
||||
* follow-up cleanup paths (wl_view_destroy_backing_impl, wl_frame_destroy)
|
||||
* call wlr_scene_node_destroy on a freed node and segfault inside
|
||||
* wl_list_remove against a zeroed list link.
|
||||
*/
|
||||
static void fb_scene_buf_destroy_handler(struct wl_listener *l, void *d)
|
||||
{
|
||||
(void)d;
|
||||
struct wl_frame_buf *fb = wl_container_of(l, fb, scene_buf_destroy);
|
||||
fb->scene_buf = NULL;
|
||||
wl_list_remove(&fb->scene_buf_destroy.link);
|
||||
}
|
||||
static void fb_tree_destroy_handler(struct wl_listener *l, void *d)
|
||||
{
|
||||
(void)d;
|
||||
struct wl_frame_buf *fb = wl_container_of(l, fb, tree_destroy);
|
||||
fb->tree = NULL;
|
||||
/* The tree's destroy cascades into scene_buf — its destroy handler
|
||||
* will fire too and NULL fb->scene_buf separately. */
|
||||
wl_list_remove(&fb->tree_destroy.link);
|
||||
}
|
||||
void fb_attach_scene_buf_listener(struct wl_frame_buf *fb)
|
||||
{
|
||||
if (!fb || !fb->scene_buf) return;
|
||||
fb->scene_buf_destroy.notify = fb_scene_buf_destroy_handler;
|
||||
wl_signal_add(&fb->scene_buf->node.events.destroy,
|
||||
&fb->scene_buf_destroy);
|
||||
}
|
||||
static void fb_attach_tree_listener(struct wl_frame_buf *fb)
|
||||
{
|
||||
if (!fb || !fb->tree) return;
|
||||
fb->tree_destroy.notify = fb_tree_destroy_handler;
|
||||
wl_signal_add(&fb->tree->node.events.destroy, &fb->tree_destroy);
|
||||
}
|
||||
|
||||
/* Recursively update fb->x, fb->y for all descendants of `parent`, used
|
||||
* to keep absolute-position tracking in sync after the parent moves.
|
||||
* Hit-testing (frame_buf_at, frame_buf_button_at) and click coordinate
|
||||
* translation depend on these fields being current. */
|
||||
void
|
||||
frame_buf_update_children_abs(WNativeWindow parent_id)
|
||||
{
|
||||
struct wl_frame_buf *parent = frame_buf_find(parent_id);
|
||||
if (!parent) return;
|
||||
struct wl_frame_buf *child;
|
||||
wl_list_for_each(child, &wl_state.frame_list, link) {
|
||||
if (child->parent_id == parent_id) {
|
||||
child->x = parent->x + child->rel_x;
|
||||
child->y = parent->y + child->rel_y;
|
||||
/* Recurse into grandchildren */
|
||||
frame_buf_update_children_abs(child->id);
|
||||
}
|
||||
}
|
||||
/* Also update toplevel views parented to this frame */
|
||||
struct wl_toplevel_view *cv;
|
||||
wl_list_for_each(cv, &wl_state.toplevel_list, link) {
|
||||
if (cv->frame_id == parent_id) {
|
||||
cv->x = parent->x + cv->frame_rel_x;
|
||||
cv->y = parent->y + cv->frame_rel_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Hit-test: find the frame_buf under (px, py) that should receive the event.
|
||||
*
|
||||
* Returns the most-specific (innermost) frame_buf whose rendered pixels
|
||||
* are topmost at (px, py), so the WM event layer receives the correct
|
||||
* sub-window ID (titlebar, resizebar, button) needed to trigger move/resize.
|
||||
*
|
||||
* Scene-graph structure (level-tree model):
|
||||
*
|
||||
* scene->tree
|
||||
* level_trees[N] <- level bucket
|
||||
* fb->tree <- top-level frame grouping tree
|
||||
* fb->scene_buf <- full-frame decoration pixels
|
||||
* child_fb->scene_buf <- titlebar / resizebar sub-frames
|
||||
* v->scene_node <- client surface (returns NULL)
|
||||
*
|
||||
* Strategy:
|
||||
* 1. wlr_scene_node_at gives the topmost rendered node.
|
||||
* 2. First check if that exact node is a child frame_buf's scene_buf
|
||||
* (titlebar, resizebar, button) — if so, return it directly.
|
||||
* 3. Walk up to the fb->tree level (parent is a level_tree bucket).
|
||||
* 4. Match that window-root node to a top-level frame_buf's tree.
|
||||
* 5. If it was a client surface (v->scene_node), return NULL. */
|
||||
static struct wl_frame_buf *
|
||||
frame_buf_at(int px, int py)
|
||||
{
|
||||
if (!wl_state.scene) return NULL;
|
||||
|
||||
double sx, sy;
|
||||
struct wlr_scene_node *node = wlr_scene_node_at(
|
||||
&wl_state.scene->tree.node, (double)px, (double)py, &sx, &sy);
|
||||
if (!node) return NULL;
|
||||
|
||||
struct wl_frame_buf *fb;
|
||||
|
||||
/* Step 2: exact match — is the topmost node a child frame_buf's
|
||||
* scene_buf (no tree wrapper)? This covers titlebars, resizebars,
|
||||
* and button sub-frames, which are what the WM needs for move/resize. */
|
||||
wl_list_for_each(fb, &wl_state.frame_list, link) {
|
||||
if (!fb->tree && fb->scene_buf && &fb->scene_buf->node == node)
|
||||
return fb;
|
||||
}
|
||||
|
||||
/* Step 3: walk up until the parent is a level_tree bucket.
|
||||
* The node we stop at is the fb->tree of a top-level frame, or
|
||||
* a v->scene_node sitting directly in a level tree (unframed). */
|
||||
struct wlr_scene_node *n = node;
|
||||
while (n->parent
|
||||
&& &n->parent->node != &wl_state.scene->tree.node) {
|
||||
int _at_level = 0, _li;
|
||||
for (_li = 0; _li < WL_NUM_LEVEL_BUCKETS; _li++) {
|
||||
if (wl_state.level_trees[_li] &&
|
||||
n->parent == wl_state.level_trees[_li]) {
|
||||
_at_level = 1; break;
|
||||
}
|
||||
}
|
||||
if (_at_level) break;
|
||||
n = &n->parent->node;
|
||||
}
|
||||
|
||||
/* Step 4: match window-root node to a top-level frame_buf. */
|
||||
wl_list_for_each(fb, &wl_state.frame_list, link) {
|
||||
if (fb->tree && &fb->tree->node == n)
|
||||
return fb;
|
||||
}
|
||||
|
||||
/* Step 5: match a tree-less frame_buf (WINGs dialog) whose scene_buf
|
||||
* is a direct child of a level_tree bucket. */
|
||||
wl_list_for_each(fb, &wl_state.frame_list, link) {
|
||||
if (!fb->tree && fb->scene_buf && &fb->scene_buf->node == n)
|
||||
return fb;
|
||||
}
|
||||
|
||||
/* Step 6: topmost node was a client surface — not a WM frame. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* frame_buf_button_at — coordinate-based hit-test for virtual button frame_bufs.
|
||||
*
|
||||
* Buttons are composited into the titlebar buffer: their scene_buf nodes are
|
||||
* in the scene graph but carry no committed buffer (scene_buf->buffer == NULL)
|
||||
* and are therefore invisible to wlr_scene_node_at. We find them by walking
|
||||
* the frame_list and comparing absolute screen coordinates.
|
||||
*
|
||||
* Returns the button fb whose absolute rect encloses (px,py) with the
|
||||
* smallest area (most specific), or NULL if no button is under the pointer.
|
||||
*/
|
||||
static struct wl_frame_buf *
|
||||
frame_buf_button_at(int px, int py)
|
||||
{
|
||||
struct wl_frame_buf *best = NULL;
|
||||
struct wl_frame_buf *fb;
|
||||
wl_list_for_each(fb, &wl_state.frame_list, link) {
|
||||
if (!fb->parent_id) continue; /* top-level root */
|
||||
if (!fb->scene_buf) continue; /* no scene node */
|
||||
if (!fb->scene_buf->node.enabled) continue; /* hidden */
|
||||
/* Walk up the scene tree — if any ancestor is disabled, this
|
||||
* frame_buf is not actually visible (just its node enabled flag
|
||||
* is set, but a parent tree was disabled e.g. by window_unmap). */
|
||||
struct wlr_scene_node *n = fb->scene_buf->node.parent
|
||||
? &fb->scene_buf->node.parent->node : NULL;
|
||||
int ancestor_disabled = 0;
|
||||
while (n) {
|
||||
if (!n->enabled) { ancestor_disabled = 1; break; }
|
||||
n = n->parent ? &n->parent->node : NULL;
|
||||
}
|
||||
if (ancestor_disabled) continue;
|
||||
/* Buttons are small children of the titlebar with enabled scene_bufs.
|
||||
* Skip large nodes (titlebars, resizebars) by size threshold. */
|
||||
if (px < fb->x || px >= fb->x + fb->width) continue;
|
||||
if (py < fb->y || py >= fb->y + fb->height) continue;
|
||||
/* Prefer the smallest (most specific) enclosing fb. */
|
||||
if (!best || (fb->width * fb->height) < (best->width * best->height))
|
||||
best = fb;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/*
|
||||
* frame_buf_hit — resolve the WM sub-window at (px,py).
|
||||
*
|
||||
* Tries button hit-testing first (buttons are invisible to the scene graph
|
||||
* because their pixels are composited into the titlebar buffer), then falls
|
||||
* back to the scene-graph-based frame_buf_at for titlebars, resizebars, and
|
||||
* top-level frames.
|
||||
*/
|
||||
static struct wl_frame_buf *
|
||||
frame_buf_hit(int px, int py)
|
||||
{
|
||||
struct wl_frame_buf *fb = frame_buf_button_at(px, py);
|
||||
return fb ? fb : frame_buf_at(px, py);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Frame / window lifecycle */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static WNativeWindow
|
||||
wl_frame_create_toplevel(WScreen *scr,
|
||||
int x, int y, int width, int height,
|
||||
int border_width,
|
||||
int depth, void *visual,
|
||||
unsigned long colormap,
|
||||
WMPixel border_pixel)
|
||||
{
|
||||
(void)border_width; (void)depth; (void)visual;
|
||||
(void)colormap; (void)border_pixel; (void)scr;
|
||||
|
||||
if (width <= 0) width = 1;
|
||||
if (height <= 0) height = 1;
|
||||
|
||||
struct wl_frame_buf *fb = wmalloc(sizeof(*fb));
|
||||
memset(fb, 0, sizeof(*fb));
|
||||
|
||||
fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
|
||||
width, height, NULL, 0);
|
||||
if (!fb->image) {
|
||||
wfree(fb);
|
||||
return WNATIVE_NONE;
|
||||
}
|
||||
|
||||
wlr_buffer_init(&fb->base, &frame_buf_impl, width, height);
|
||||
|
||||
fb->id = wl_state.next_synthetic_id++;
|
||||
fb->x = x;
|
||||
fb->y = y;
|
||||
fb->width = width;
|
||||
fb->height = height;
|
||||
|
||||
/* Create scene buffer node under a per-window tree.
|
||||
* The tree is positioned at (x,y) so all children use local offsets.
|
||||
* Default to WL_LAYER_NORMAL; wl_stacking_set_level() will reparent
|
||||
* to the correct bucket when AddToStackList() fires immediately after. */
|
||||
if (wl_state.scene) {
|
||||
struct wlr_scene_tree *_parent = wl_state.level_trees[WL_LAYER_NORMAL]
|
||||
? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree;
|
||||
fb->tree = wlr_scene_tree_create(_parent);
|
||||
if (fb->tree) {
|
||||
fb_attach_tree_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->tree->node, x, y);
|
||||
fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base);
|
||||
if (fb->scene_buf) {
|
||||
fb_attach_scene_buf_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0);
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
/* Tree starts disabled — wl_window_map enables it when the
|
||||
* frame is logically mapped. Without this, decoration
|
||||
* children (titlebar, buttons) of unmapped menus/dialogs
|
||||
* appear as orphans because window_map_raised on those
|
||||
* children enables their scene_bufs while the frame itself
|
||||
* has not been mapped. */
|
||||
wlr_scene_node_set_enabled(&fb->tree->node, false);
|
||||
}
|
||||
}
|
||||
|
||||
wl_list_insert(&wl_state.frame_list, &fb->link);
|
||||
/* Register this frame_buf image in the WINGs backing table so
|
||||
* WMDrawString() can resolve the window ID to a pixman_image_t
|
||||
* (needed for miniwindow title drawing in update_icon_title). */
|
||||
W_RegisterBacking(fb->id, fb->image);
|
||||
return fb->id;
|
||||
}
|
||||
|
||||
static WNativeWindow
|
||||
wl_frame_create_child(WNativeWindow parent_win, WScreen *scr,
|
||||
int x, int y, int width, int height)
|
||||
{
|
||||
(void)scr;
|
||||
|
||||
if (width <= 0) width = 1;
|
||||
if (height <= 0) height = 1;
|
||||
|
||||
/* Compute absolute position from parent */
|
||||
int abs_x = x, abs_y = y;
|
||||
struct wl_frame_buf *parent = frame_buf_find(parent_win);
|
||||
if (parent) {
|
||||
abs_x = parent->x + x;
|
||||
abs_y = parent->y + y;
|
||||
}
|
||||
|
||||
struct wl_frame_buf *fb = wmalloc(sizeof(*fb));
|
||||
memset(fb, 0, sizeof(*fb));
|
||||
|
||||
fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
|
||||
width, height, NULL, 0);
|
||||
if (!fb->image) {
|
||||
wfree(fb);
|
||||
return WNATIVE_NONE;
|
||||
}
|
||||
|
||||
wlr_buffer_init(&fb->base, &frame_buf_impl, width, height);
|
||||
|
||||
fb->id = wl_state.next_synthetic_id++;
|
||||
fb->parent_id = parent_win;
|
||||
fb->rel_x = x;
|
||||
fb->rel_y = y;
|
||||
fb->x = abs_x;
|
||||
fb->y = abs_y;
|
||||
fb->width = width;
|
||||
fb->height = height;
|
||||
|
||||
if (wl_state.scene) {
|
||||
struct wlr_scene_tree *ptree = (parent && parent->tree)
|
||||
? parent->tree : &wl_state.scene->tree;
|
||||
fb->scene_buf = wlr_scene_buffer_create(ptree, &fb->base);
|
||||
if (fb->scene_buf) {
|
||||
fb_attach_scene_buf_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, x, y);
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
}
|
||||
|
||||
wl_list_insert(&wl_state.frame_list, &fb->link);
|
||||
W_RegisterBacking(fb->id, fb->image);
|
||||
return fb->id;
|
||||
}
|
||||
|
||||
static void
|
||||
wl_frame_destroy(WScreen *scr, WNativeWindow win)
|
||||
{
|
||||
(void)scr;
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (!fb) return;
|
||||
W_UnregisterBacking(fb->id);
|
||||
/* Destroy the scene_buf and (if any) the tree. Without destroying
|
||||
* the tree, leaked tree nodes accumulate in the scene graph and
|
||||
* can cause stale rendering or hit-testing issues (e.g. ghost
|
||||
* titlebars after a menu is closed). */
|
||||
if (fb->scene_buf)
|
||||
wlr_scene_node_destroy(&fb->scene_buf->node);
|
||||
if (fb->tree)
|
||||
wlr_scene_node_destroy(&fb->tree->node);
|
||||
wl_list_remove(&fb->link);
|
||||
wlr_buffer_drop(&fb->base);
|
||||
}
|
||||
|
||||
static void
|
||||
wl_frame_configure(WNativeWindow win, int x, int y, int width, int height)
|
||||
{
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (!fb) return;
|
||||
|
||||
/* Update position tracking */
|
||||
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 within parent tree */
|
||||
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);
|
||||
}
|
||||
|
||||
/* Resize: reallocate the pixman image if dimensions changed. */
|
||||
if (width > 0 && height > 0 && (width != fb->width || height != fb->height)) {
|
||||
if (fb->image)
|
||||
pixman_image_unref(fb->image);
|
||||
fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
|
||||
width, height, NULL, 0);
|
||||
fb->width = width;
|
||||
fb->height = height;
|
||||
fb->base.width = width;
|
||||
fb->base.height = height;
|
||||
/* Notify scene graph of new size — required for hit-testing.
|
||||
* wlroots caches buffer dimensions; without this, clicks beyond
|
||||
* the old size would miss this scene_buf. */
|
||||
if (fb->scene_buf)
|
||||
wlr_scene_buffer_set_dest_size(fb->scene_buf, width, height);
|
||||
}
|
||||
|
||||
/* Update absolute position tracking — recursively for all descendants. */
|
||||
frame_buf_update_children_abs(win);
|
||||
}
|
||||
|
||||
static void wl_frame_paint_button(WCoreWindow *button, WTexture *texture,
|
||||
unsigned long color, WPixmap *image, int pushed);
|
||||
|
||||
/*
|
||||
* wl_frame_paint — render the entire frame decoration into the pixman buffer.
|
||||
*
|
||||
* Uses wTextureRenderImage() (pure CPU) for titlebar/resizebar backgrounds,
|
||||
* pixman for bevel lines, and the WINGs font backend for title text.
|
||||
* Returns 1 (handled) so wFrameWindowPaint skips the Xlib code path.
|
||||
*/
|
||||
static int
|
||||
wl_frame_paint(struct WFrameWindow *fwin)
|
||||
{
|
||||
struct wl_frame_buf *fb = frame_buf_find(fwin->core->window);
|
||||
if (!fb || !fb->image)
|
||||
return 0;
|
||||
|
||||
int state = fwin->flags.state;
|
||||
int total_w = fb->width;
|
||||
int total_h = fb->height;
|
||||
/* Clear the buffer to transparent black. */
|
||||
pixman_color_t clear = {0, 0, 0, 0}; /* transparent */
|
||||
pixman_image_fill_rectangles(PIXMAN_OP_SRC, fb->image, &clear,
|
||||
1, &(pixman_rectangle16_t){0, 0, total_w, total_h});
|
||||
|
||||
/* --- Titlebar --- */
|
||||
if (fwin->titlebar && fwin->top_width > 0) {
|
||||
struct wl_frame_buf *tb_fb = frame_buf_find(fwin->titlebar->window);
|
||||
pixman_image_t *tb_img = tb_fb ? tb_fb->image : fb->image;
|
||||
int tb_w = total_w; /* always full frame width */
|
||||
int tb_h = fwin->top_width;
|
||||
|
||||
/* In Wayland we composite buttons into the titlebar buffer, so
|
||||
* the titlebar must be full-width and positioned at the frame origin
|
||||
* regardless of what updateTitlebar() configured for X11 layout. */
|
||||
if (tb_fb) {
|
||||
tb_fb->rel_x = 0;
|
||||
tb_fb->rel_y = 0;
|
||||
tb_fb->x = fb->x;
|
||||
tb_fb->y = fb->y;
|
||||
if (tb_fb->scene_buf)
|
||||
wlr_scene_node_set_position(&tb_fb->scene_buf->node, 0, 0);
|
||||
}
|
||||
|
||||
/* Ensure titlebar buffer is full width */
|
||||
if (tb_fb && tb_fb->width < tb_w) {
|
||||
pixman_image_unref(tb_fb->image);
|
||||
tb_fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
|
||||
tb_w, tb_h, NULL, 0);
|
||||
tb_fb->width = tb_w;
|
||||
tb_fb->height = tb_h;
|
||||
tb_fb->base.width = tb_w;
|
||||
tb_fb->base.height = tb_h;
|
||||
tb_img = tb_fb->image;
|
||||
/* Notify scene graph so hit-testing covers the new size. */
|
||||
if (tb_fb->scene_buf)
|
||||
wlr_scene_buffer_set_dest_size(tb_fb->scene_buf, tb_w, tb_h);
|
||||
}
|
||||
WTexture *tex = fwin->title_texture[state];
|
||||
|
||||
/* Clear titlebar buffer */
|
||||
if (tb_fb) {
|
||||
pixman_color_t clear = {0, 0, 0, 0};
|
||||
pixman_image_fill_rectangles(PIXMAN_OP_SRC, tb_img, &clear,
|
||||
1, &(pixman_rectangle16_t){0, 0, tb_w, tb_h});
|
||||
}
|
||||
|
||||
/* Render texture to RImage. */
|
||||
RImage *img = wTextureRenderImage(tex, tb_w, tb_h, WREL_RAISED);
|
||||
if (img) {
|
||||
uint32_t *dst = pixman_image_get_data(tb_img);
|
||||
int dst_stride = pixman_image_get_stride(tb_img) / 4;
|
||||
int x, y;
|
||||
if (img->format == RRGBAFormat) {
|
||||
for (y = 0; y < tb_h && y < img->height; y++) {
|
||||
unsigned char *src_row = img->data + y * img->width * 4;
|
||||
uint32_t *dst_row = dst + y * dst_stride;
|
||||
for (x = 0; x < tb_w && x < img->width; x++) {
|
||||
unsigned char r = src_row[x*4+0];
|
||||
unsigned char g = src_row[x*4+1];
|
||||
unsigned char b = src_row[x*4+2];
|
||||
unsigned char a = src_row[x*4+3];
|
||||
dst_row[x] = ((uint32_t)a << 24) | ((uint32_t)r << 16) |
|
||||
((uint32_t)g << 8) | (uint32_t)b;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* RGB format */
|
||||
for (y = 0; y < tb_h && y < img->height; y++) {
|
||||
unsigned char *src_row = img->data + y * img->width * 3;
|
||||
uint32_t *dst_row = dst + y * dst_stride;
|
||||
for (x = 0; x < tb_w && x < img->width; x++) {
|
||||
unsigned char r = src_row[x*3+0];
|
||||
unsigned char g = src_row[x*3+1];
|
||||
unsigned char b = src_row[x*3+2];
|
||||
dst_row[x] = 0xFF000000U | ((uint32_t)r << 16) |
|
||||
((uint32_t)g << 8) | (uint32_t)b;
|
||||
}
|
||||
}
|
||||
}
|
||||
RReleaseImage(img);
|
||||
}
|
||||
|
||||
/* Draw title text. */
|
||||
if (fwin->title && *fwin->font && fwin->title_color[state]) {
|
||||
int lofs = 6, rofs = 6;
|
||||
int titlelen, tw, tx, ty, th;
|
||||
char *title;
|
||||
|
||||
if (fwin->left_button && !fwin->flags.hide_left_button &&
|
||||
!fwin->flags.lbutton_dont_fit)
|
||||
lofs += fwin->left_button->width + 3;
|
||||
if (fwin->right_button && !fwin->flags.hide_right_button &&
|
||||
!fwin->flags.rbutton_dont_fit)
|
||||
rofs += fwin->right_button->width + 3;
|
||||
|
||||
title = ShrinkString(*fwin->font, fwin->title, tb_w - lofs - rofs);
|
||||
titlelen = strlen(title);
|
||||
tw = WMWidthOfString(*fwin->font, title, titlelen);
|
||||
th = WMFontHeight(*fwin->font);
|
||||
|
||||
switch (fwin->flags.justification) {
|
||||
case WTJ_LEFT: tx = lofs; break;
|
||||
case WTJ_RIGHT: tx = tb_w - tw - rofs; break;
|
||||
default: tx = (tb_w - tw) / 2; break;
|
||||
}
|
||||
ty = (tb_h - th) / 2;
|
||||
|
||||
WMDrawString(fwin->screen_ptr->wmscreen, (WMDrawable)tb_img,
|
||||
fwin->title_color[state], *fwin->font,
|
||||
tx, ty, title, titlelen);
|
||||
wfree(title);
|
||||
}
|
||||
|
||||
/* Commit titlebar buffer */
|
||||
if (tb_fb && tb_fb->scene_buf) {
|
||||
wlr_scene_buffer_set_buffer_with_damage(tb_fb->scene_buf, &tb_fb->base, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Titlebar buttons --- */
|
||||
if (fwin->titlebar && fwin->top_width > 0) {
|
||||
if (fwin->left_button && !fwin->flags.hide_left_button &&
|
||||
!fwin->flags.lbutton_dont_fit)
|
||||
wl_frame_paint_button(fwin->left_button,
|
||||
fwin->title_texture[state],
|
||||
WMColorPixel(fwin->title_color[state]),
|
||||
fwin->lbutton_image, False);
|
||||
if (fwin->right_button && !fwin->flags.hide_right_button &&
|
||||
!fwin->flags.rbutton_dont_fit)
|
||||
wl_frame_paint_button(fwin->right_button,
|
||||
fwin->title_texture[state],
|
||||
WMColorPixel(fwin->title_color[state]),
|
||||
fwin->rbutton_image, False);
|
||||
}
|
||||
|
||||
/* --- Resizebar --- */
|
||||
/* Skip resizebar when frame is shaded (height ≤ titlebar). */
|
||||
bool show_resizebar = fwin->resizebar && fwin->bottom_width > 0
|
||||
&& total_h > (int)fwin->top_width;
|
||||
if (show_resizebar) {
|
||||
int rb_y = total_h - fwin->bottom_width;
|
||||
int rb_w = total_w;
|
||||
int rb_h = fwin->bottom_width;
|
||||
WTexture *tex = fwin->resizebar_texture[0];
|
||||
|
||||
RImage *img = wTextureRenderImage(tex, rb_w, rb_h, WREL_FLAT);
|
||||
if (img) {
|
||||
uint32_t *dst = pixman_image_get_data(fb->image);
|
||||
int dst_stride = pixman_image_get_stride(fb->image) / 4;
|
||||
int x, y;
|
||||
if (img->format == RRGBAFormat) {
|
||||
for (y = 0; y < rb_h && y < img->height; y++) {
|
||||
unsigned char *src_row = img->data + y * img->width * 4;
|
||||
uint32_t *dst_row = dst + (rb_y + y) * dst_stride;
|
||||
for (x = 0; x < rb_w && x < img->width; x++) {
|
||||
unsigned char r = src_row[x*4+0];
|
||||
unsigned char g = src_row[x*4+1];
|
||||
unsigned char b = src_row[x*4+2];
|
||||
unsigned char a = src_row[x*4+3];
|
||||
dst_row[x] = ((uint32_t)a << 24) | ((uint32_t)r << 16) |
|
||||
((uint32_t)g << 8) | (uint32_t)b;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < rb_h && y < img->height; y++) {
|
||||
unsigned char *src_row = img->data + y * img->width * 3;
|
||||
uint32_t *dst_row = dst + (rb_y + y) * dst_stride;
|
||||
for (x = 0; x < rb_w && x < img->width; x++) {
|
||||
unsigned char r = src_row[x*3+0];
|
||||
unsigned char g = src_row[x*3+1];
|
||||
unsigned char b = src_row[x*3+2];
|
||||
dst_row[x] = 0xFF000000U | ((uint32_t)r << 16) |
|
||||
((uint32_t)g << 8) | (uint32_t)b;
|
||||
}
|
||||
}
|
||||
}
|
||||
RReleaseImage(img);
|
||||
}
|
||||
|
||||
/* Draw resizebar corner grab indicators */
|
||||
int cw = fwin->resizebar_corner_width;
|
||||
uint32_t *dst = pixman_image_get_data(fb->image);
|
||||
int dst_stride = pixman_image_get_stride(fb->image) / 4;
|
||||
int midx = (rb_w < 1) ? 0 : rb_w / 2;
|
||||
int midy = (rb_h < 1) ? 0 : rb_h / 2;
|
||||
uint32_t base_px = dst[(rb_y + midy) * dst_stride + midx];
|
||||
unsigned char br = (base_px >> 16) & 0xFF;
|
||||
unsigned char bg = (base_px >> 8) & 0xFF;
|
||||
unsigned char bb = (base_px) & 0xFF;
|
||||
uint32_t dim_px = 0xFF000000U |
|
||||
(((unsigned)((br > 40) ? (br - 40) : 0) << 16)) |
|
||||
(((unsigned)((bg > 40) ? (bg - 40) : 0) << 8)) |
|
||||
((unsigned)((bb > 40) ? (bb - 40) : 0));
|
||||
uint32_t light_px = 0xFF000000U |
|
||||
(((unsigned)((br < 175) ? (br + 80) : 255) << 16)) |
|
||||
(((unsigned)((bg < 175) ? (bg + 80) : 255) << 8)) |
|
||||
((unsigned)((bb < 175) ? (bb + 80) : 255));
|
||||
|
||||
/* Top bevel: dim at y=0, light at y=1 */
|
||||
for (int x = 0; x < rb_w; x++) {
|
||||
dst[(rb_y + 0) * dst_stride + x] = dim_px;
|
||||
dst[(rb_y + 1) * dst_stride + x] = light_px;
|
||||
}
|
||||
|
||||
/* Left corner grab handle: vertical lines from y=2 to rb_h-1 */
|
||||
int xl = (cw < rb_w) ? cw : rb_w - 1;
|
||||
if (xl > 0) {
|
||||
for (int y = 2; y < rb_h; y++) {
|
||||
dst[(rb_y + y) * dst_stride + xl - 1] = dim_px;
|
||||
dst[(rb_y + y) * dst_stride + xl] = light_px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Right corner grab handle */
|
||||
int xr = (rb_w - cw - 2 >= 0) ? (rb_w - cw - 2) : 0;
|
||||
if (xr + 1 < rb_w) {
|
||||
for (int y = 2; y < rb_h; y++) {
|
||||
dst[(rb_y + y) * dst_stride + xr] = dim_px;
|
||||
dst[(rb_y + y) * dst_stride + xr + 1] = light_px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide the resizebar's own scene node when shaded. */
|
||||
if (fwin->resizebar) {
|
||||
struct wl_frame_buf *rb_fb = frame_buf_find(fwin->resizebar->window);
|
||||
if (rb_fb && rb_fb->scene_buf)
|
||||
wlr_scene_node_set_enabled(&rb_fb->scene_buf->node, show_resizebar);
|
||||
}
|
||||
|
||||
/* Commit the buffer to the scene graph only if it has content (resizebar). */
|
||||
if (fb->scene_buf) {
|
||||
if (show_resizebar) {
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, true);
|
||||
wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL);
|
||||
} else {
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (wl_state.output)
|
||||
wlr_output_schedule_frame(wl_state.output);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Focus */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -7238,7 +6534,7 @@ static void wl_noop_frame_alloc_border_pixel(unsigned long colormap,
|
|||
|
||||
static void wl_noop_frame_update_texture(struct WFrameWindow *fwin) { (void)fwin; }
|
||||
|
||||
static void wl_frame_paint_button(WCoreWindow *button, WTexture *texture,
|
||||
void wl_frame_paint_button(WCoreWindow *button, WTexture *texture,
|
||||
unsigned long color, WPixmap *image, int pushed)
|
||||
{
|
||||
(void)texture; (void)image;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,729 @@
|
|||
#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_buffer.h>
|
||||
#include <wlr/interfaces/wlr_buffer.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <pixman.h>
|
||||
#include "../../screen.h"
|
||||
#include "../../framewin.h"
|
||||
#include "../../texture.h"
|
||||
#include "../../window.h"
|
||||
#include "../../misc.h"
|
||||
#include "wl_types.h"
|
||||
#include <WINGs/WINGsP.h>
|
||||
|
||||
/* Forward declarations from WINGs */
|
||||
extern void *W_RegisterBacking(unsigned long id, void *img);
|
||||
extern void W_UnregisterBacking(unsigned long id);
|
||||
extern void *W_GetViewBacking(unsigned long id);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
void frame_buf_destroy(struct wlr_buffer *wlr_buf)
|
||||
{
|
||||
struct wl_frame_buf *fb = wl_container_of(wlr_buf, fb, base);
|
||||
if (fb->image)
|
||||
pixman_image_unref(fb->image);
|
||||
wfree(fb);
|
||||
}
|
||||
|
||||
bool frame_buf_begin_data_ptr_access(struct wlr_buffer *wlr_buf,
|
||||
uint32_t flags, void **data, uint32_t *format, size_t *stride)
|
||||
{
|
||||
struct wl_frame_buf *fb = wl_container_of(wlr_buf, fb, base);
|
||||
(void)flags;
|
||||
*data = pixman_image_get_data(fb->image);
|
||||
*format = DRM_FORMAT_ARGB8888;
|
||||
*stride = (size_t)pixman_image_get_stride(fb->image);
|
||||
return true;
|
||||
}
|
||||
|
||||
void frame_buf_end_data_ptr_access(struct wlr_buffer *wlr_buf)
|
||||
{
|
||||
(void)wlr_buf;
|
||||
}
|
||||
|
||||
const struct wlr_buffer_impl frame_buf_impl = {
|
||||
.destroy = frame_buf_destroy,
|
||||
.begin_data_ptr_access = frame_buf_begin_data_ptr_access,
|
||||
.end_data_ptr_access = frame_buf_end_data_ptr_access,
|
||||
};
|
||||
|
||||
struct wl_frame_buf *
|
||||
frame_buf_find(WNativeWindow id)
|
||||
{
|
||||
struct wl_frame_buf *fb;
|
||||
wl_list_for_each(fb, &wl_state.frame_list, link) {
|
||||
if (fb->id == id) return fb;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* fb_scene_buf_attach / fb_tree_attach
|
||||
*
|
||||
* Register destroy listeners on a scene_buf or tree so that fb->scene_buf
|
||||
* (or fb->tree) is NULL'd when wlroots auto-destroys the node — typically
|
||||
* via cascade when an ancestor scene_tree is destroyed. Without this,
|
||||
* follow-up cleanup paths (wl_view_destroy_backing_impl, wl_frame_destroy)
|
||||
* call wlr_scene_node_destroy on a freed node and segfault inside
|
||||
* wl_list_remove against a zeroed list link.
|
||||
*/
|
||||
void fb_scene_buf_destroy_handler(struct wl_listener *l, void *d)
|
||||
{
|
||||
(void)d;
|
||||
struct wl_frame_buf *fb = wl_container_of(l, fb, scene_buf_destroy);
|
||||
fb->scene_buf = NULL;
|
||||
wl_list_remove(&fb->scene_buf_destroy.link);
|
||||
}
|
||||
void fb_tree_destroy_handler(struct wl_listener *l, void *d)
|
||||
{
|
||||
(void)d;
|
||||
struct wl_frame_buf *fb = wl_container_of(l, fb, tree_destroy);
|
||||
fb->tree = NULL;
|
||||
/* The tree's destroy cascades into scene_buf — its destroy handler
|
||||
* will fire too and NULL fb->scene_buf separately. */
|
||||
wl_list_remove(&fb->tree_destroy.link);
|
||||
}
|
||||
void fb_attach_scene_buf_listener(struct wl_frame_buf *fb)
|
||||
{
|
||||
if (!fb || !fb->scene_buf) return;
|
||||
fb->scene_buf_destroy.notify = fb_scene_buf_destroy_handler;
|
||||
wl_signal_add(&fb->scene_buf->node.events.destroy,
|
||||
&fb->scene_buf_destroy);
|
||||
}
|
||||
void fb_attach_tree_listener(struct wl_frame_buf *fb)
|
||||
{
|
||||
if (!fb || !fb->tree) return;
|
||||
fb->tree_destroy.notify = fb_tree_destroy_handler;
|
||||
wl_signal_add(&fb->tree->node.events.destroy, &fb->tree_destroy);
|
||||
}
|
||||
|
||||
/* Recursively update fb->x, fb->y for all descendants of `parent`, used
|
||||
* to keep absolute-position tracking in sync after the parent moves.
|
||||
* Hit-testing (frame_buf_at, frame_buf_button_at) and click coordinate
|
||||
* translation depend on these fields being current. */
|
||||
void
|
||||
frame_buf_update_children_abs(WNativeWindow parent_id)
|
||||
{
|
||||
struct wl_frame_buf *parent = frame_buf_find(parent_id);
|
||||
if (!parent) return;
|
||||
struct wl_frame_buf *child;
|
||||
wl_list_for_each(child, &wl_state.frame_list, link) {
|
||||
if (child->parent_id == parent_id) {
|
||||
child->x = parent->x + child->rel_x;
|
||||
child->y = parent->y + child->rel_y;
|
||||
/* Recurse into grandchildren */
|
||||
frame_buf_update_children_abs(child->id);
|
||||
}
|
||||
}
|
||||
/* Also update toplevel views parented to this frame */
|
||||
struct wl_toplevel_view *cv;
|
||||
wl_list_for_each(cv, &wl_state.toplevel_list, link) {
|
||||
if (cv->frame_id == parent_id) {
|
||||
cv->x = parent->x + cv->frame_rel_x;
|
||||
cv->y = parent->y + cv->frame_rel_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Hit-test: find the frame_buf under (px, py) that should receive the event.
|
||||
*
|
||||
* Returns the most-specific (innermost) frame_buf whose rendered pixels
|
||||
* are topmost at (px, py), so the WM event layer receives the correct
|
||||
* sub-window ID (titlebar, resizebar, button) needed to trigger move/resize.
|
||||
*
|
||||
* Scene-graph structure (level-tree model):
|
||||
*
|
||||
* scene->tree
|
||||
* level_trees[N] <- level bucket
|
||||
* fb->tree <- top-level frame grouping tree
|
||||
* fb->scene_buf <- full-frame decoration pixels
|
||||
* child_fb->scene_buf <- titlebar / resizebar sub-frames
|
||||
* v->scene_node <- client surface (returns NULL)
|
||||
*
|
||||
* Strategy:
|
||||
* 1. wlr_scene_node_at gives the topmost rendered node.
|
||||
* 2. First check if that exact node is a child frame_buf's scene_buf
|
||||
* (titlebar, resizebar, button) — if so, return it directly.
|
||||
* 3. Walk up to the fb->tree level (parent is a level_tree bucket).
|
||||
* 4. Match that window-root node to a top-level frame_buf's tree.
|
||||
* 5. If it was a client surface (v->scene_node), return NULL. */
|
||||
struct wl_frame_buf *
|
||||
frame_buf_at(int px, int py)
|
||||
{
|
||||
if (!wl_state.scene) return NULL;
|
||||
|
||||
double sx, sy;
|
||||
struct wlr_scene_node *node = wlr_scene_node_at(
|
||||
&wl_state.scene->tree.node, (double)px, (double)py, &sx, &sy);
|
||||
if (!node) return NULL;
|
||||
|
||||
struct wl_frame_buf *fb;
|
||||
|
||||
/* Step 2: exact match — is the topmost node a child frame_buf's
|
||||
* scene_buf (no tree wrapper)? This covers titlebars, resizebars,
|
||||
* and button sub-frames, which are what the WM needs for move/resize. */
|
||||
wl_list_for_each(fb, &wl_state.frame_list, link) {
|
||||
if (!fb->tree && fb->scene_buf && &fb->scene_buf->node == node)
|
||||
return fb;
|
||||
}
|
||||
|
||||
/* Step 3: walk up until the parent is a level_tree bucket.
|
||||
* The node we stop at is the fb->tree of a top-level frame, or
|
||||
* a v->scene_node sitting directly in a level tree (unframed). */
|
||||
struct wlr_scene_node *n = node;
|
||||
while (n->parent
|
||||
&& &n->parent->node != &wl_state.scene->tree.node) {
|
||||
int _at_level = 0, _li;
|
||||
for (_li = 0; _li < WL_NUM_LEVEL_BUCKETS; _li++) {
|
||||
if (wl_state.level_trees[_li] &&
|
||||
n->parent == wl_state.level_trees[_li]) {
|
||||
_at_level = 1; break;
|
||||
}
|
||||
}
|
||||
if (_at_level) break;
|
||||
n = &n->parent->node;
|
||||
}
|
||||
|
||||
/* Step 4: match window-root node to a top-level frame_buf. */
|
||||
wl_list_for_each(fb, &wl_state.frame_list, link) {
|
||||
if (fb->tree && &fb->tree->node == n)
|
||||
return fb;
|
||||
}
|
||||
|
||||
/* Step 5: match a tree-less frame_buf (WINGs dialog) whose scene_buf
|
||||
* is a direct child of a level_tree bucket. */
|
||||
wl_list_for_each(fb, &wl_state.frame_list, link) {
|
||||
if (!fb->tree && fb->scene_buf && &fb->scene_buf->node == n)
|
||||
return fb;
|
||||
}
|
||||
|
||||
/* Step 6: topmost node was a client surface — not a WM frame. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* frame_buf_button_at — coordinate-based hit-test for virtual button frame_bufs.
|
||||
*
|
||||
* Buttons are composited into the titlebar buffer: their scene_buf nodes are
|
||||
* in the scene graph but carry no committed buffer (scene_buf->buffer == NULL)
|
||||
* and are therefore invisible to wlr_scene_node_at. We find them by walking
|
||||
* the frame_list and comparing absolute screen coordinates.
|
||||
*
|
||||
* Returns the button fb whose absolute rect encloses (px,py) with the
|
||||
* smallest area (most specific), or NULL if no button is under the pointer.
|
||||
*/
|
||||
struct wl_frame_buf *
|
||||
frame_buf_button_at(int px, int py)
|
||||
{
|
||||
struct wl_frame_buf *best = NULL;
|
||||
struct wl_frame_buf *fb;
|
||||
wl_list_for_each(fb, &wl_state.frame_list, link) {
|
||||
if (!fb->parent_id) continue; /* top-level root */
|
||||
if (!fb->scene_buf) continue; /* no scene node */
|
||||
if (!fb->scene_buf->node.enabled) continue; /* hidden */
|
||||
/* Walk up the scene tree — if any ancestor is disabled, this
|
||||
* frame_buf is not actually visible (just its node enabled flag
|
||||
* is set, but a parent tree was disabled e.g. by window_unmap). */
|
||||
struct wlr_scene_node *n = fb->scene_buf->node.parent
|
||||
? &fb->scene_buf->node.parent->node : NULL;
|
||||
int ancestor_disabled = 0;
|
||||
while (n) {
|
||||
if (!n->enabled) { ancestor_disabled = 1; break; }
|
||||
n = n->parent ? &n->parent->node : NULL;
|
||||
}
|
||||
if (ancestor_disabled) continue;
|
||||
/* Buttons are small children of the titlebar with enabled scene_bufs.
|
||||
* Skip large nodes (titlebars, resizebars) by size threshold. */
|
||||
if (px < fb->x || px >= fb->x + fb->width) continue;
|
||||
if (py < fb->y || py >= fb->y + fb->height) continue;
|
||||
/* Prefer the smallest (most specific) enclosing fb. */
|
||||
if (!best || (fb->width * fb->height) < (best->width * best->height))
|
||||
best = fb;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/*
|
||||
* frame_buf_hit — resolve the WM sub-window at (px,py).
|
||||
*
|
||||
* Tries button hit-testing first (buttons are invisible to the scene graph
|
||||
* because their pixels are composited into the titlebar buffer), then falls
|
||||
* back to the scene-graph-based frame_buf_at for titlebars, resizebars, and
|
||||
* top-level frames.
|
||||
*/
|
||||
struct wl_frame_buf *
|
||||
frame_buf_hit(int px, int py)
|
||||
{
|
||||
struct wl_frame_buf *fb = frame_buf_button_at(px, py);
|
||||
return fb ? fb : frame_buf_at(px, py);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Frame / window lifecycle */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
WNativeWindow
|
||||
wl_frame_create_toplevel(WScreen *scr,
|
||||
int x, int y, int width, int height,
|
||||
int border_width,
|
||||
int depth, void *visual,
|
||||
unsigned long colormap,
|
||||
WMPixel border_pixel)
|
||||
{
|
||||
(void)border_width; (void)depth; (void)visual;
|
||||
(void)colormap; (void)border_pixel; (void)scr;
|
||||
|
||||
if (width <= 0) width = 1;
|
||||
if (height <= 0) height = 1;
|
||||
|
||||
struct wl_frame_buf *fb = wmalloc(sizeof(*fb));
|
||||
memset(fb, 0, sizeof(*fb));
|
||||
|
||||
fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
|
||||
width, height, NULL, 0);
|
||||
if (!fb->image) {
|
||||
wfree(fb);
|
||||
return WNATIVE_NONE;
|
||||
}
|
||||
|
||||
wlr_buffer_init(&fb->base, &frame_buf_impl, width, height);
|
||||
|
||||
fb->id = wl_state.next_synthetic_id++;
|
||||
fb->x = x;
|
||||
fb->y = y;
|
||||
fb->width = width;
|
||||
fb->height = height;
|
||||
|
||||
/* Create scene buffer node under a per-window tree.
|
||||
* The tree is positioned at (x,y) so all children use local offsets.
|
||||
* Default to WL_LAYER_NORMAL; wl_stacking_set_level() will reparent
|
||||
* to the correct bucket when AddToStackList() fires immediately after. */
|
||||
if (wl_state.scene) {
|
||||
struct wlr_scene_tree *_parent = wl_state.level_trees[WL_LAYER_NORMAL]
|
||||
? wl_state.level_trees[WL_LAYER_NORMAL] : &wl_state.scene->tree;
|
||||
fb->tree = wlr_scene_tree_create(_parent);
|
||||
if (fb->tree) {
|
||||
fb_attach_tree_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->tree->node, x, y);
|
||||
fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base);
|
||||
if (fb->scene_buf) {
|
||||
fb_attach_scene_buf_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0);
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
/* Tree starts disabled — wl_window_map enables it when the
|
||||
* frame is logically mapped. Without this, decoration
|
||||
* children (titlebar, buttons) of unmapped menus/dialogs
|
||||
* appear as orphans because window_map_raised on those
|
||||
* children enables their scene_bufs while the frame itself
|
||||
* has not been mapped. */
|
||||
wlr_scene_node_set_enabled(&fb->tree->node, false);
|
||||
}
|
||||
}
|
||||
|
||||
wl_list_insert(&wl_state.frame_list, &fb->link);
|
||||
/* Register this frame_buf image in the WINGs backing table so
|
||||
* WMDrawString() can resolve the window ID to a pixman_image_t
|
||||
* (needed for miniwindow title drawing in update_icon_title). */
|
||||
W_RegisterBacking(fb->id, fb->image);
|
||||
return fb->id;
|
||||
}
|
||||
|
||||
WNativeWindow
|
||||
wl_frame_create_child(WNativeWindow parent_win, WScreen *scr,
|
||||
int x, int y, int width, int height)
|
||||
{
|
||||
(void)scr;
|
||||
|
||||
if (width <= 0) width = 1;
|
||||
if (height <= 0) height = 1;
|
||||
|
||||
/* Compute absolute position from parent */
|
||||
int abs_x = x, abs_y = y;
|
||||
struct wl_frame_buf *parent = frame_buf_find(parent_win);
|
||||
if (parent) {
|
||||
abs_x = parent->x + x;
|
||||
abs_y = parent->y + y;
|
||||
}
|
||||
|
||||
struct wl_frame_buf *fb = wmalloc(sizeof(*fb));
|
||||
memset(fb, 0, sizeof(*fb));
|
||||
|
||||
fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
|
||||
width, height, NULL, 0);
|
||||
if (!fb->image) {
|
||||
wfree(fb);
|
||||
return WNATIVE_NONE;
|
||||
}
|
||||
|
||||
wlr_buffer_init(&fb->base, &frame_buf_impl, width, height);
|
||||
|
||||
fb->id = wl_state.next_synthetic_id++;
|
||||
fb->parent_id = parent_win;
|
||||
fb->rel_x = x;
|
||||
fb->rel_y = y;
|
||||
fb->x = abs_x;
|
||||
fb->y = abs_y;
|
||||
fb->width = width;
|
||||
fb->height = height;
|
||||
|
||||
if (wl_state.scene) {
|
||||
struct wlr_scene_tree *ptree = (parent && parent->tree)
|
||||
? parent->tree : &wl_state.scene->tree;
|
||||
fb->scene_buf = wlr_scene_buffer_create(ptree, &fb->base);
|
||||
if (fb->scene_buf) {
|
||||
fb_attach_scene_buf_listener(fb);
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, x, y);
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
}
|
||||
|
||||
wl_list_insert(&wl_state.frame_list, &fb->link);
|
||||
W_RegisterBacking(fb->id, fb->image);
|
||||
return fb->id;
|
||||
}
|
||||
|
||||
void
|
||||
wl_frame_destroy(WScreen *scr, WNativeWindow win)
|
||||
{
|
||||
(void)scr;
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (!fb) return;
|
||||
W_UnregisterBacking(fb->id);
|
||||
/* Destroy the scene_buf and (if any) the tree. Without destroying
|
||||
* the tree, leaked tree nodes accumulate in the scene graph and
|
||||
* can cause stale rendering or hit-testing issues (e.g. ghost
|
||||
* titlebars after a menu is closed). */
|
||||
if (fb->scene_buf)
|
||||
wlr_scene_node_destroy(&fb->scene_buf->node);
|
||||
if (fb->tree)
|
||||
wlr_scene_node_destroy(&fb->tree->node);
|
||||
wl_list_remove(&fb->link);
|
||||
wlr_buffer_drop(&fb->base);
|
||||
}
|
||||
|
||||
void
|
||||
wl_frame_configure(WNativeWindow win, int x, int y, int width, int height)
|
||||
{
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (!fb) return;
|
||||
|
||||
/* Update position tracking */
|
||||
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 within parent tree */
|
||||
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);
|
||||
}
|
||||
|
||||
/* Resize: reallocate the pixman image if dimensions changed. */
|
||||
if (width > 0 && height > 0 && (width != fb->width || height != fb->height)) {
|
||||
if (fb->image)
|
||||
pixman_image_unref(fb->image);
|
||||
fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
|
||||
width, height, NULL, 0);
|
||||
fb->width = width;
|
||||
fb->height = height;
|
||||
fb->base.width = width;
|
||||
fb->base.height = height;
|
||||
/* Notify scene graph of new size — required for hit-testing.
|
||||
* wlroots caches buffer dimensions; without this, clicks beyond
|
||||
* the old size would miss this scene_buf. */
|
||||
if (fb->scene_buf)
|
||||
wlr_scene_buffer_set_dest_size(fb->scene_buf, width, height);
|
||||
}
|
||||
|
||||
/* Update absolute position tracking — recursively for all descendants. */
|
||||
frame_buf_update_children_abs(win);
|
||||
}
|
||||
|
||||
void wl_frame_paint_button(WCoreWindow *button, WTexture *texture,
|
||||
unsigned long color, WPixmap *image, int pushed);
|
||||
|
||||
/*
|
||||
* wl_frame_paint — render the entire frame decoration into the pixman buffer.
|
||||
*
|
||||
* Uses wTextureRenderImage() (pure CPU) for titlebar/resizebar backgrounds,
|
||||
* pixman for bevel lines, and the WINGs font backend for title text.
|
||||
* Returns 1 (handled) so wFrameWindowPaint skips the Xlib code path.
|
||||
*/
|
||||
int
|
||||
wl_frame_paint(struct WFrameWindow *fwin)
|
||||
{
|
||||
struct wl_frame_buf *fb = frame_buf_find(fwin->core->window);
|
||||
if (!fb || !fb->image)
|
||||
return 0;
|
||||
|
||||
int state = fwin->flags.state;
|
||||
int total_w = fb->width;
|
||||
int total_h = fb->height;
|
||||
/* Clear the buffer to transparent black. */
|
||||
pixman_color_t clear = {0, 0, 0, 0}; /* transparent */
|
||||
pixman_image_fill_rectangles(PIXMAN_OP_SRC, fb->image, &clear,
|
||||
1, &(pixman_rectangle16_t){0, 0, total_w, total_h});
|
||||
|
||||
/* --- Titlebar --- */
|
||||
if (fwin->titlebar && fwin->top_width > 0) {
|
||||
struct wl_frame_buf *tb_fb = frame_buf_find(fwin->titlebar->window);
|
||||
pixman_image_t *tb_img = tb_fb ? tb_fb->image : fb->image;
|
||||
int tb_w = total_w; /* always full frame width */
|
||||
int tb_h = fwin->top_width;
|
||||
|
||||
/* In Wayland we composite buttons into the titlebar buffer, so
|
||||
* the titlebar must be full-width and positioned at the frame origin
|
||||
* regardless of what updateTitlebar() configured for X11 layout. */
|
||||
if (tb_fb) {
|
||||
tb_fb->rel_x = 0;
|
||||
tb_fb->rel_y = 0;
|
||||
tb_fb->x = fb->x;
|
||||
tb_fb->y = fb->y;
|
||||
if (tb_fb->scene_buf)
|
||||
wlr_scene_node_set_position(&tb_fb->scene_buf->node, 0, 0);
|
||||
}
|
||||
|
||||
/* Ensure titlebar buffer is full width */
|
||||
if (tb_fb && tb_fb->width < tb_w) {
|
||||
pixman_image_unref(tb_fb->image);
|
||||
tb_fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8,
|
||||
tb_w, tb_h, NULL, 0);
|
||||
tb_fb->width = tb_w;
|
||||
tb_fb->height = tb_h;
|
||||
tb_fb->base.width = tb_w;
|
||||
tb_fb->base.height = tb_h;
|
||||
tb_img = tb_fb->image;
|
||||
/* Notify scene graph so hit-testing covers the new size. */
|
||||
if (tb_fb->scene_buf)
|
||||
wlr_scene_buffer_set_dest_size(tb_fb->scene_buf, tb_w, tb_h);
|
||||
}
|
||||
WTexture *tex = fwin->title_texture[state];
|
||||
|
||||
/* Clear titlebar buffer */
|
||||
if (tb_fb) {
|
||||
pixman_color_t clear = {0, 0, 0, 0};
|
||||
pixman_image_fill_rectangles(PIXMAN_OP_SRC, tb_img, &clear,
|
||||
1, &(pixman_rectangle16_t){0, 0, tb_w, tb_h});
|
||||
}
|
||||
|
||||
/* Render texture to RImage. */
|
||||
RImage *img = wTextureRenderImage(tex, tb_w, tb_h, WREL_RAISED);
|
||||
if (img) {
|
||||
uint32_t *dst = pixman_image_get_data(tb_img);
|
||||
int dst_stride = pixman_image_get_stride(tb_img) / 4;
|
||||
int x, y;
|
||||
if (img->format == RRGBAFormat) {
|
||||
for (y = 0; y < tb_h && y < img->height; y++) {
|
||||
unsigned char *src_row = img->data + y * img->width * 4;
|
||||
uint32_t *dst_row = dst + y * dst_stride;
|
||||
for (x = 0; x < tb_w && x < img->width; x++) {
|
||||
unsigned char r = src_row[x*4+0];
|
||||
unsigned char g = src_row[x*4+1];
|
||||
unsigned char b = src_row[x*4+2];
|
||||
unsigned char a = src_row[x*4+3];
|
||||
dst_row[x] = ((uint32_t)a << 24) | ((uint32_t)r << 16) |
|
||||
((uint32_t)g << 8) | (uint32_t)b;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* RGB format */
|
||||
for (y = 0; y < tb_h && y < img->height; y++) {
|
||||
unsigned char *src_row = img->data + y * img->width * 3;
|
||||
uint32_t *dst_row = dst + y * dst_stride;
|
||||
for (x = 0; x < tb_w && x < img->width; x++) {
|
||||
unsigned char r = src_row[x*3+0];
|
||||
unsigned char g = src_row[x*3+1];
|
||||
unsigned char b = src_row[x*3+2];
|
||||
dst_row[x] = 0xFF000000U | ((uint32_t)r << 16) |
|
||||
((uint32_t)g << 8) | (uint32_t)b;
|
||||
}
|
||||
}
|
||||
}
|
||||
RReleaseImage(img);
|
||||
}
|
||||
|
||||
/* Draw title text. */
|
||||
if (fwin->title && *fwin->font && fwin->title_color[state]) {
|
||||
int lofs = 6, rofs = 6;
|
||||
int titlelen, tw, tx, ty, th;
|
||||
char *title;
|
||||
|
||||
if (fwin->left_button && !fwin->flags.hide_left_button &&
|
||||
!fwin->flags.lbutton_dont_fit)
|
||||
lofs += fwin->left_button->width + 3;
|
||||
if (fwin->right_button && !fwin->flags.hide_right_button &&
|
||||
!fwin->flags.rbutton_dont_fit)
|
||||
rofs += fwin->right_button->width + 3;
|
||||
|
||||
title = ShrinkString(*fwin->font, fwin->title, tb_w - lofs - rofs);
|
||||
titlelen = strlen(title);
|
||||
tw = WMWidthOfString(*fwin->font, title, titlelen);
|
||||
th = WMFontHeight(*fwin->font);
|
||||
|
||||
switch (fwin->flags.justification) {
|
||||
case WTJ_LEFT: tx = lofs; break;
|
||||
case WTJ_RIGHT: tx = tb_w - tw - rofs; break;
|
||||
default: tx = (tb_w - tw) / 2; break;
|
||||
}
|
||||
ty = (tb_h - th) / 2;
|
||||
|
||||
WMDrawString(fwin->screen_ptr->wmscreen, (WMDrawable)tb_img,
|
||||
fwin->title_color[state], *fwin->font,
|
||||
tx, ty, title, titlelen);
|
||||
wfree(title);
|
||||
}
|
||||
|
||||
/* Commit titlebar buffer */
|
||||
if (tb_fb && tb_fb->scene_buf) {
|
||||
wlr_scene_buffer_set_buffer_with_damage(tb_fb->scene_buf, &tb_fb->base, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Titlebar buttons --- */
|
||||
if (fwin->titlebar && fwin->top_width > 0) {
|
||||
if (fwin->left_button && !fwin->flags.hide_left_button &&
|
||||
!fwin->flags.lbutton_dont_fit)
|
||||
wl_frame_paint_button(fwin->left_button,
|
||||
fwin->title_texture[state],
|
||||
WMColorPixel(fwin->title_color[state]),
|
||||
fwin->lbutton_image, False);
|
||||
if (fwin->right_button && !fwin->flags.hide_right_button &&
|
||||
!fwin->flags.rbutton_dont_fit)
|
||||
wl_frame_paint_button(fwin->right_button,
|
||||
fwin->title_texture[state],
|
||||
WMColorPixel(fwin->title_color[state]),
|
||||
fwin->rbutton_image, False);
|
||||
}
|
||||
|
||||
/* --- Resizebar --- */
|
||||
/* Skip resizebar when frame is shaded (height ≤ titlebar). */
|
||||
bool show_resizebar = fwin->resizebar && fwin->bottom_width > 0
|
||||
&& total_h > (int)fwin->top_width;
|
||||
if (show_resizebar) {
|
||||
int rb_y = total_h - fwin->bottom_width;
|
||||
int rb_w = total_w;
|
||||
int rb_h = fwin->bottom_width;
|
||||
WTexture *tex = fwin->resizebar_texture[0];
|
||||
|
||||
RImage *img = wTextureRenderImage(tex, rb_w, rb_h, WREL_FLAT);
|
||||
if (img) {
|
||||
uint32_t *dst = pixman_image_get_data(fb->image);
|
||||
int dst_stride = pixman_image_get_stride(fb->image) / 4;
|
||||
int x, y;
|
||||
if (img->format == RRGBAFormat) {
|
||||
for (y = 0; y < rb_h && y < img->height; y++) {
|
||||
unsigned char *src_row = img->data + y * img->width * 4;
|
||||
uint32_t *dst_row = dst + (rb_y + y) * dst_stride;
|
||||
for (x = 0; x < rb_w && x < img->width; x++) {
|
||||
unsigned char r = src_row[x*4+0];
|
||||
unsigned char g = src_row[x*4+1];
|
||||
unsigned char b = src_row[x*4+2];
|
||||
unsigned char a = src_row[x*4+3];
|
||||
dst_row[x] = ((uint32_t)a << 24) | ((uint32_t)r << 16) |
|
||||
((uint32_t)g << 8) | (uint32_t)b;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < rb_h && y < img->height; y++) {
|
||||
unsigned char *src_row = img->data + y * img->width * 3;
|
||||
uint32_t *dst_row = dst + (rb_y + y) * dst_stride;
|
||||
for (x = 0; x < rb_w && x < img->width; x++) {
|
||||
unsigned char r = src_row[x*3+0];
|
||||
unsigned char g = src_row[x*3+1];
|
||||
unsigned char b = src_row[x*3+2];
|
||||
dst_row[x] = 0xFF000000U | ((uint32_t)r << 16) |
|
||||
((uint32_t)g << 8) | (uint32_t)b;
|
||||
}
|
||||
}
|
||||
}
|
||||
RReleaseImage(img);
|
||||
}
|
||||
|
||||
/* Draw resizebar corner grab indicators */
|
||||
int cw = fwin->resizebar_corner_width;
|
||||
uint32_t *dst = pixman_image_get_data(fb->image);
|
||||
int dst_stride = pixman_image_get_stride(fb->image) / 4;
|
||||
int midx = (rb_w < 1) ? 0 : rb_w / 2;
|
||||
int midy = (rb_h < 1) ? 0 : rb_h / 2;
|
||||
uint32_t base_px = dst[(rb_y + midy) * dst_stride + midx];
|
||||
unsigned char br = (base_px >> 16) & 0xFF;
|
||||
unsigned char bg = (base_px >> 8) & 0xFF;
|
||||
unsigned char bb = (base_px) & 0xFF;
|
||||
uint32_t dim_px = 0xFF000000U |
|
||||
(((unsigned)((br > 40) ? (br - 40) : 0) << 16)) |
|
||||
(((unsigned)((bg > 40) ? (bg - 40) : 0) << 8)) |
|
||||
((unsigned)((bb > 40) ? (bb - 40) : 0));
|
||||
uint32_t light_px = 0xFF000000U |
|
||||
(((unsigned)((br < 175) ? (br + 80) : 255) << 16)) |
|
||||
(((unsigned)((bg < 175) ? (bg + 80) : 255) << 8)) |
|
||||
((unsigned)((bb < 175) ? (bb + 80) : 255));
|
||||
|
||||
/* Top bevel: dim at y=0, light at y=1 */
|
||||
for (int x = 0; x < rb_w; x++) {
|
||||
dst[(rb_y + 0) * dst_stride + x] = dim_px;
|
||||
dst[(rb_y + 1) * dst_stride + x] = light_px;
|
||||
}
|
||||
|
||||
/* Left corner grab handle: vertical lines from y=2 to rb_h-1 */
|
||||
int xl = (cw < rb_w) ? cw : rb_w - 1;
|
||||
if (xl > 0) {
|
||||
for (int y = 2; y < rb_h; y++) {
|
||||
dst[(rb_y + y) * dst_stride + xl - 1] = dim_px;
|
||||
dst[(rb_y + y) * dst_stride + xl] = light_px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Right corner grab handle */
|
||||
int xr = (rb_w - cw - 2 >= 0) ? (rb_w - cw - 2) : 0;
|
||||
if (xr + 1 < rb_w) {
|
||||
for (int y = 2; y < rb_h; y++) {
|
||||
dst[(rb_y + y) * dst_stride + xr] = dim_px;
|
||||
dst[(rb_y + y) * dst_stride + xr + 1] = light_px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide the resizebar's own scene node when shaded. */
|
||||
if (fwin->resizebar) {
|
||||
struct wl_frame_buf *rb_fb = frame_buf_find(fwin->resizebar->window);
|
||||
if (rb_fb && rb_fb->scene_buf)
|
||||
wlr_scene_node_set_enabled(&rb_fb->scene_buf->node, show_resizebar);
|
||||
}
|
||||
|
||||
/* Commit the buffer to the scene graph only if it has content (resizebar). */
|
||||
if (fb->scene_buf) {
|
||||
if (show_resizebar) {
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, true);
|
||||
wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL);
|
||||
} else {
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (wl_state.output)
|
||||
wlr_output_schedule_frame(wl_state.output);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -483,3 +483,18 @@ void wl_icccm_set_class_hint_if_absent(WNativeWindow win, const char *wm_class,
|
|||
void wl_icccm_set_command(WNativeWindow win, char **argv, int argc);
|
||||
int wl_icccm_get_command(WNativeWindow win, char ***argv_out, int *argc_out);
|
||||
void wl_icccm_free_command(char **argv);
|
||||
|
||||
/* Frame buffer module declarations (extracted to wl_framebuf.c) */
|
||||
struct wl_frame_buf *frame_buf_find(WNativeWindow id);
|
||||
struct wl_frame_buf *frame_buf_at(int px, int py);
|
||||
struct wl_frame_buf *frame_buf_button_at(int px, int py);
|
||||
struct wl_frame_buf *frame_buf_hit(int px, int py);
|
||||
WNativeWindow wl_frame_create_toplevel(WScreen *scr, int x, int y, int width, int height, int border_width, int depth, void *visual, unsigned long colormap, WMPixel border_pixel);
|
||||
WNativeWindow wl_frame_create_child(WNativeWindow parent_win, WScreen *scr, int x, int y, int width, int height);
|
||||
void wl_frame_destroy(WScreen *scr, WNativeWindow win);
|
||||
void wl_frame_configure(WNativeWindow win, int x, int y, int width, int height);
|
||||
int wl_frame_paint(struct WFrameWindow *fwin);
|
||||
extern const struct wlr_buffer_impl frame_buf_impl;
|
||||
void fb_attach_tree_listener(struct wl_frame_buf *fb);
|
||||
void fb_attach_scene_buf_listener(struct wl_frame_buf *fb);
|
||||
void wl_frame_paint_button(WCoreWindow *button, WTexture *texture, unsigned long color, WPixmap *image, int pushed);
|
||||
|
|
|
|||
Loading…
Reference in New Issue