wayland: fix close/iconify buttons and miniwindow rendering
Three bugs fixed: 1. Close and iconify buttons broken after frame rendering model change Buttons are composited into the titlebar buffer, so their scene_buf nodes carry no committed buffer and are invisible to wlr_scene_node_at. frame_buf_at() returned the titlebar fb for all button-area clicks, routing them to titlebarMouseDown instead of buttonMouseDown. Fix: add frame_buf_button_at() -- coordinate walk of frame_list entries with scene_buf->buffer == NULL -- and frame_buf_hit() which tries button resolution first then falls back to the scene graph. Replace all frame_buf_at() calls in button and motion handlers with frame_buf_hit(). Also emit synthetic WME_LEAVE/WME_ENTER events on sub-window boundary crossings so buttonMouseDown's press-track loop works correctly. 2. Iconify crash (SIGSEGV in update_icon_title) WMDrawString() was passed icon->core->window (a frame_buf synthetic ID, 0x10000000+) as the drawable. wl_font_resolve_drawable() only recognised WINGs view IDs (0x20000000+), so it cast the frame_buf ID directly as a pixman_image_t* -- wild pointer -- SIGSEGV. Fix: add W_RegisterBacking()/W_UnregisterBacking() to wview_wl.c. Register each frame_buf's pixman image in the backing table on creation and unregister on destroy. Extend the ID range check in wfont_wl.c and wrender_wl.c from 0x20000000 down to 0x10000000 so frame_buf IDs resolve correctly via W_GetViewBacking(). 3. Resizebar visible after iconify + miniwindow not rendering wl_window_unmap() disabled fb->scene_buf->node individually, but a subsequent wl_frame_paint() could re-enable and re-commit it while the window was still iconified. Fix: for top-level frames, disable fb->tree->node -- hides the entire scene tree (frame buffer, titlebar, resizebar, client surface) atomically. Symmetric enable in wl_window_map(). Miniwindow icons were invisible: icon_paint was a noop so the frame_buf image was never committed to the scene graph. Add wl_icon_paint() mirroring wl_appicon_paint(): tile + file_image composite + title text, committed via wlr_scene_buffer_set_buffer(). Wire into wIconPaint() in icon.c so the backend finalises the surface after policy-layer setup.
This commit is contained in:
parent
a961c86d4b
commit
d29f2a29c9
|
|
@ -23,7 +23,11 @@
|
|||
extern void *W_GetViewBacking(unsigned long id);
|
||||
static inline pixman_image_t *wl_font_resolve_drawable(WMDrawable d)
|
||||
{
|
||||
if ((unsigned long)d >= 0x20000000 && (unsigned long)d < 0x30000000)
|
||||
/* Synthetic IDs: 0x10000000-0x1FFFFFFF = WM frame_buf windows,
|
||||
* 0x20000000-0x2FFFFFFF = WINGs view windows.
|
||||
* Both are registered via W_RegisterBacking so W_GetViewBacking
|
||||
* handles both ranges uniformly. */
|
||||
if ((unsigned long)d >= 0x10000000 && (unsigned long)d < 0x30000000)
|
||||
return (pixman_image_t *)W_GetViewBacking((unsigned long)d);
|
||||
return (pixman_image_t *)(uintptr_t)d;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,14 @@ render_wl_screen_fini(void *rctx)
|
|||
}
|
||||
|
||||
/* Resolve a WMDrawable to a pixman_image_t*.
|
||||
* If it's a WINGs view ID (0x20000000..0x2FFFFFFF), look up backing image. */
|
||||
* Synthetic IDs: 0x10000000-0x1FFFFFFF = WM frame_buf windows,
|
||||
* 0x20000000-0x2FFFFFFF = WINGs view windows.
|
||||
* Both registered via W_RegisterBacking. */
|
||||
extern void *W_GetViewBacking(unsigned long id);
|
||||
static inline pixman_image_t *
|
||||
resolve_drawable(WMDrawable d)
|
||||
{
|
||||
if ((unsigned long)d >= 0x20000000 && (unsigned long)d < 0x30000000)
|
||||
if ((unsigned long)d >= 0x10000000 && (unsigned long)d < 0x30000000)
|
||||
return (pixman_image_t *)W_GetViewBacking((unsigned long)d);
|
||||
return (pixman_image_t *)d;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,6 +91,24 @@ static void view_table_remove(Window id)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* W_RegisterBacking - register a pixman_image_t backing for any synthetic
|
||||
* window ID (WINGs views OR WM frame_buf windows). Used by wl_backend.c so
|
||||
* that WMDrawString() can resolve a WM window ID to a pixman_image_t when
|
||||
* drawing text into frame_buf images (e.g. miniwindow title).
|
||||
*/
|
||||
void W_RegisterBacking(Window id, void *img)
|
||||
{
|
||||
view_table_put(id, NULL);
|
||||
W_SetViewBacking(id, img);
|
||||
}
|
||||
|
||||
/* W_UnregisterBacking - remove a backing registration (on frame_buf destroy). */
|
||||
void W_UnregisterBacking(Window id)
|
||||
{
|
||||
view_table_remove(id);
|
||||
}
|
||||
|
||||
static void *wl_screen_init(W_Screen *scr)
|
||||
{
|
||||
(void)scr;
|
||||
|
|
|
|||
|
|
@ -213,6 +213,8 @@ void W_WL_SetLoopDispatch(struct W_Screen *scr,
|
|||
int loop_fd,
|
||||
void (*dispatch_fn)(int timeout_ms, void *ud),
|
||||
void *userdata);
|
||||
void W_RegisterBacking(unsigned long id, void *img);
|
||||
void W_UnregisterBacking(unsigned long id);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Forward declarations */
|
||||
|
|
@ -542,6 +544,11 @@ struct wl_compositor_state {
|
|||
RImage *btn_close_img;
|
||||
RImage *btn_iconify_img;
|
||||
RImage *btn_kill_img;
|
||||
/* Tracks the frame_buf the pointer was last over (after button resolution),
|
||||
* so motion handlers can emit synthetic WME_LEAVE/WME_ENTER events when
|
||||
* the pointer crosses between WM frame sub-windows (titlebar, resizebar,
|
||||
* buttons). buttonMouseDown's inner press-track loop depends on these. */
|
||||
WNativeWindow last_hovered_id;
|
||||
};
|
||||
|
||||
static struct wl_compositor_state wl_state;
|
||||
|
|
@ -3909,6 +3916,50 @@ frame_buf_at(int px, int py)
|
|||
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->buffer) continue; /* has active pixels — titlebar/resizebar */
|
||||
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 */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -3964,6 +4015,10 @@ wl_frame_create_toplevel(WScreen *scr,
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -4016,6 +4071,7 @@ wl_frame_create_child(WNativeWindow parent_win, WScreen *scr,
|
|||
}
|
||||
|
||||
wl_list_insert(&wl_state.frame_list, &fb->link);
|
||||
W_RegisterBacking(fb->id, fb->image);
|
||||
return fb->id;
|
||||
}
|
||||
|
||||
|
|
@ -4025,6 +4081,7 @@ 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);
|
||||
if (fb->scene_buf)
|
||||
wlr_scene_node_destroy(&fb->scene_buf->node);
|
||||
wl_list_remove(&fb->link);
|
||||
|
|
@ -4442,19 +4499,23 @@ handle_pointer_motion(struct wl_listener *listener, void *data)
|
|||
if (wl_state.cursor)
|
||||
wlr_cursor_warp(wl_state.cursor, NULL,
|
||||
(double)wl_state.pointer_x, (double)wl_state.pointer_y);
|
||||
/* Phase A: enqueue motion event for WINGs */
|
||||
/* Phase A: enqueue motion event for WINGs.
|
||||
* Use frame_buf_hit so button sub-windows (which have no active scene_buf
|
||||
* buffer and are invisible to wlr_scene_node_at) are resolved correctly. */
|
||||
WMScreen *wmscr = wl_get_wmscreen();
|
||||
if (wmscr) {
|
||||
WMEvent wev;
|
||||
memset(&wev, 0, sizeof(wev));
|
||||
wev.type = WME_MOTION;
|
||||
struct wl_frame_buf *fb = frame_buf_at(wl_state.pointer_x,
|
||||
wl_state.pointer_y);
|
||||
struct wl_frame_buf *fb = frame_buf_hit(wl_state.pointer_x,
|
||||
wl_state.pointer_y);
|
||||
WNativeWindow new_id;
|
||||
if (fb) {
|
||||
wev.window = fb->id;
|
||||
wev.u.motion.window = fb->id;
|
||||
wev.u.motion.x = wl_state.pointer_x - fb->x;
|
||||
wev.u.motion.y = wl_state.pointer_y - fb->y;
|
||||
new_id = fb->id;
|
||||
} else {
|
||||
WScreen *scr0 = wScreenWithNumber(0);
|
||||
WNativeWindow root = scr0 ? scr0->root_win : (WNativeWindow)0x0FFFFFFF;
|
||||
|
|
@ -4462,11 +4523,39 @@ handle_pointer_motion(struct wl_listener *listener, void *data)
|
|||
wev.u.motion.window = root;
|
||||
wev.u.motion.x = wl_state.pointer_x;
|
||||
wev.u.motion.y = wl_state.pointer_y;
|
||||
new_id = root;
|
||||
}
|
||||
wev.u.motion.time = ev->time_msec;
|
||||
wev.u.motion.x_root = wl_state.pointer_x;
|
||||
wev.u.motion.y_root = wl_state.pointer_y;
|
||||
W_WL_EnqueueEvent(wmscr, &wev, NULL);
|
||||
/* Emit synthetic LEAVE/ENTER when pointer crosses WM sub-window
|
||||
* boundaries (titlebar ↔ button, etc.). These feed the inner
|
||||
* press-track loop in framewin.c:buttonMouseDown so the cancel-
|
||||
* on-leave behaviour works correctly. */
|
||||
if (new_id != wl_state.last_hovered_id) {
|
||||
WMEvent xev;
|
||||
memset(&xev, 0, sizeof(xev));
|
||||
if (wl_state.last_hovered_id) {
|
||||
xev.type = WME_LEAVE;
|
||||
xev.window = wl_state.last_hovered_id;
|
||||
xev.u.crossing.window = wl_state.last_hovered_id;
|
||||
xev.u.crossing.x_root = wl_state.pointer_x;
|
||||
xev.u.crossing.y_root = wl_state.pointer_y;
|
||||
xev.u.crossing.mode = NotifyNormal;
|
||||
xev.u.crossing.detail = NotifyAncestor;
|
||||
W_WL_EnqueueEvent(wmscr, &xev, NULL);
|
||||
}
|
||||
xev.type = WME_ENTER;
|
||||
xev.window = new_id;
|
||||
xev.u.crossing.window = new_id;
|
||||
xev.u.crossing.x_root = wl_state.pointer_x;
|
||||
xev.u.crossing.y_root = wl_state.pointer_y;
|
||||
xev.u.crossing.mode = NotifyNormal;
|
||||
xev.u.crossing.detail = NotifyAncestor;
|
||||
W_WL_EnqueueEvent(wmscr, &xev, NULL);
|
||||
wl_state.last_hovered_id = new_id;
|
||||
}
|
||||
}
|
||||
/* Phase 22: apply grab before updating normal pointer focus. */
|
||||
if (wl_state.grab_type) { wl_grab_apply_motion(); return; }
|
||||
|
|
@ -4495,19 +4584,23 @@ handle_pointer_motion_absolute(struct wl_listener *listener, void *data)
|
|||
if (wl_state.pointer_x >= ow) wl_state.pointer_x = ow - 1;
|
||||
if (wl_state.pointer_y >= oh) wl_state.pointer_y = oh - 1;
|
||||
}
|
||||
/* Phase A: enqueue motion event for WINGs */
|
||||
/* Phase A: enqueue motion event for WINGs.
|
||||
* Use frame_buf_hit so button sub-windows (which have no active scene_buf
|
||||
* buffer and are invisible to wlr_scene_node_at) are resolved correctly. */
|
||||
WMScreen *wmscr2 = wl_get_wmscreen();
|
||||
if (wmscr2) {
|
||||
WMEvent wev;
|
||||
memset(&wev, 0, sizeof(wev));
|
||||
wev.type = WME_MOTION;
|
||||
struct wl_frame_buf *fb = frame_buf_at(wl_state.pointer_x,
|
||||
wl_state.pointer_y);
|
||||
struct wl_frame_buf *fb = frame_buf_hit(wl_state.pointer_x,
|
||||
wl_state.pointer_y);
|
||||
WNativeWindow new_id;
|
||||
if (fb) {
|
||||
wev.window = fb->id;
|
||||
wev.u.motion.window = fb->id;
|
||||
wev.u.motion.x = wl_state.pointer_x - fb->x;
|
||||
wev.u.motion.y = wl_state.pointer_y - fb->y;
|
||||
new_id = fb->id;
|
||||
} else {
|
||||
WScreen *scr0 = wScreenWithNumber(0);
|
||||
WNativeWindow root = scr0 ? scr0->root_win : (WNativeWindow)0x0FFFFFFF;
|
||||
|
|
@ -4515,11 +4608,36 @@ handle_pointer_motion_absolute(struct wl_listener *listener, void *data)
|
|||
wev.u.motion.window = root;
|
||||
wev.u.motion.x = wl_state.pointer_x;
|
||||
wev.u.motion.y = wl_state.pointer_y;
|
||||
new_id = root;
|
||||
}
|
||||
wev.u.motion.time = ev->time_msec;
|
||||
wev.u.motion.x_root = wl_state.pointer_x;
|
||||
wev.u.motion.y_root = wl_state.pointer_y;
|
||||
W_WL_EnqueueEvent(wmscr2, &wev, NULL);
|
||||
/* Emit synthetic LEAVE/ENTER on sub-window boundary crossings. */
|
||||
if (new_id != wl_state.last_hovered_id) {
|
||||
WMEvent xev;
|
||||
memset(&xev, 0, sizeof(xev));
|
||||
if (wl_state.last_hovered_id) {
|
||||
xev.type = WME_LEAVE;
|
||||
xev.window = wl_state.last_hovered_id;
|
||||
xev.u.crossing.window = wl_state.last_hovered_id;
|
||||
xev.u.crossing.x_root = wl_state.pointer_x;
|
||||
xev.u.crossing.y_root = wl_state.pointer_y;
|
||||
xev.u.crossing.mode = NotifyNormal;
|
||||
xev.u.crossing.detail = NotifyAncestor;
|
||||
W_WL_EnqueueEvent(wmscr2, &xev, NULL);
|
||||
}
|
||||
xev.type = WME_ENTER;
|
||||
xev.window = new_id;
|
||||
xev.u.crossing.window = new_id;
|
||||
xev.u.crossing.x_root = wl_state.pointer_x;
|
||||
xev.u.crossing.y_root = wl_state.pointer_y;
|
||||
xev.u.crossing.mode = NotifyNormal;
|
||||
xev.u.crossing.detail = NotifyAncestor;
|
||||
W_WL_EnqueueEvent(wmscr2, &xev, NULL);
|
||||
wl_state.last_hovered_id = new_id;
|
||||
}
|
||||
}
|
||||
/* Phase 22: apply grab before updating normal pointer focus. */
|
||||
if (wl_state.grab_type) { wl_grab_apply_motion(); return; }
|
||||
|
|
@ -5059,9 +5177,13 @@ handle_pointer_button(struct wl_listener *listener, void *data)
|
|||
wev.type = (ev->state == WLR_BUTTON_PRESSED)
|
||||
? WME_BUTTON_PRESS : WME_BUTTON_RELEASE;
|
||||
|
||||
/* Hit-test: determine which WM window was clicked */
|
||||
struct wl_frame_buf *fb = frame_buf_at(wl_state.pointer_x,
|
||||
wl_state.pointer_y);
|
||||
/* Hit-test: determine which WM window was clicked.
|
||||
* Use frame_buf_hit so button sub-windows (invisible to the scene
|
||||
* graph because their pixels are composited into the titlebar buffer)
|
||||
* are resolved to their own window IDs, letting handleButtonPress
|
||||
* route the event to buttonMouseDown instead of titlebarMouseDown. */
|
||||
struct wl_frame_buf *fb = frame_buf_hit(wl_state.pointer_x,
|
||||
wl_state.pointer_y);
|
||||
if (fb) {
|
||||
wev.window = fb->id;
|
||||
wev.u.button.window = fb->id;
|
||||
|
|
@ -5975,9 +6097,10 @@ wl_window_map(WNativeWindow win)
|
|||
}
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, true);
|
||||
/* Raise the tree (raises entire window assembly together) */
|
||||
if (fb->tree)
|
||||
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)
|
||||
} 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;
|
||||
|
|
@ -6036,16 +6159,20 @@ wl_window_unmap(WNativeWindow win)
|
|||
return;
|
||||
}
|
||||
struct wl_frame_buf *fb = frame_buf_find(win);
|
||||
if (fb && fb->scene_buf) {
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, false);
|
||||
/* Also hide children */
|
||||
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, false);
|
||||
}
|
||||
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);
|
||||
}
|
||||
static void wl_window_move(WNativeWindow win, int x, int y)
|
||||
{
|
||||
|
|
@ -7235,7 +7362,85 @@ static char *wl_get_command_for_window(WNativeWindow win, int elements)
|
|||
}
|
||||
|
||||
static void wl_noop_icon_update_pixmap(WIcon *icon) { (void)icon; }
|
||||
static void wl_noop_icon_paint(WIcon *icon) { (void)icon; }
|
||||
|
||||
/*
|
||||
* wl_icon_paint - render a miniwindow (iconified window) icon into its
|
||||
* frame_buf and commit it to the scene graph.
|
||||
*
|
||||
* Mirrors wl_appicon_paint: tile background + optional image + title text.
|
||||
* Called at the end of wIconPaint() in icon.c so the backend finalises the
|
||||
* surface after the policy layer has set up icon->file_image etc.
|
||||
*/
|
||||
static void
|
||||
wl_icon_paint(WIcon *icon)
|
||||
{
|
||||
if (!icon || !icon->core) return;
|
||||
|
||||
struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)icon->core->window);
|
||||
if (!fb || !fb->image) return;
|
||||
|
||||
WScreen *scr = icon->core->screen_ptr;
|
||||
if (!scr) return;
|
||||
int size = wPreferences.icon_size;
|
||||
|
||||
/* --- Tile background --- */
|
||||
RImage *tile = scr->icon_tile ? RCloneImage(scr->icon_tile) : NULL;
|
||||
if (!tile) {
|
||||
tile = RCreateImage(size, size, 1);
|
||||
if (!tile) return;
|
||||
RColor grey = {0xAE, 0xAA, 0xAE, 0xFF};
|
||||
RClearImage(tile, &grey);
|
||||
}
|
||||
|
||||
/* --- Composite icon image (file_image from database / NET_WM_ICON) --- */
|
||||
RImage *img = icon->file_image;
|
||||
if (img) {
|
||||
int theight = icon->show_title ? WMFontHeight(scr->icon_title_font) : 0;
|
||||
unsigned w = ((unsigned)img->width > (unsigned)size) ? (unsigned)size : (unsigned)img->width;
|
||||
unsigned h = ((unsigned)img->height + (unsigned)theight > (unsigned)size)
|
||||
? (unsigned)(size - theight) : (unsigned)img->height;
|
||||
int x = (size - (int)w) / 2;
|
||||
int y = theight + (size - theight - (int)h) / 2;
|
||||
int sx = ((int)img->width - (int)w) / 2;
|
||||
int sy = ((int)img->height - (int)h) / 2;
|
||||
RCombineArea(tile, img, sx, sy, w, h, x, y);
|
||||
}
|
||||
|
||||
/* --- Blit RImage (RGBA/RGB) -> pixman ARGB32 --- */
|
||||
uint32_t *dst = (uint32_t *)pixman_image_get_data(fb->image);
|
||||
int stride = pixman_image_get_stride(fb->image) / 4;
|
||||
unsigned char *tsrc = tile->data;
|
||||
int has_alpha = (tile->format == RRGBAFormat);
|
||||
for (int row = 0; row < size && row < fb->height; row++) {
|
||||
for (int col = 0; col < size && col < fb->width; col++) {
|
||||
int si = (row * tile->width + col) * (has_alpha ? 4 : 3);
|
||||
unsigned char r = tsrc[si], g = tsrc[si+1], b = tsrc[si+2];
|
||||
unsigned char a = has_alpha ? tsrc[si+3] : 0xFF;
|
||||
dst[row * stride + col] = ((uint32_t)a << 24) | ((uint32_t)r << 16)
|
||||
| ((uint32_t)g << 8) | (uint32_t)b;
|
||||
}
|
||||
}
|
||||
RReleaseImage(tile);
|
||||
|
||||
/* --- Title text --- */
|
||||
if (icon->show_title && icon->icon_name && scr->icon_title_font) {
|
||||
WMDrawable d = (WMDrawable)(uintptr_t)fb->image;
|
||||
int tw = WMWidthOfString(scr->icon_title_font, icon->icon_name,
|
||||
strlen(icon->icon_name));
|
||||
int tx = (size - tw) / 2;
|
||||
if (tx < 2) tx = 2;
|
||||
WMColor *color = scr->icon_title_color;
|
||||
if (color)
|
||||
WMDrawString(scr->wmscreen, d, color, scr->icon_title_font,
|
||||
tx, 0, icon->icon_name, strlen(icon->icon_name));
|
||||
}
|
||||
|
||||
/* --- Commit to scene graph --- */
|
||||
wlr_scene_buffer_set_buffer(fb->scene_buf, &fb->base);
|
||||
wlr_scene_node_set_enabled(&fb->scene_buf->node, true);
|
||||
if (fb->tree)
|
||||
wlr_scene_node_set_enabled(&fb->tree->node, true);
|
||||
}
|
||||
static void wl_noop_appicon_show(WApplication *wapp) { (void)wapp; }
|
||||
static void wl_appicon_paint(WAppIcon *aicon)
|
||||
{
|
||||
|
|
@ -7954,7 +8159,7 @@ const WMBackend wl_backend = {
|
|||
.get_shortcut_string = wl_noop_get_shortcut_string,
|
||||
.get_command_for_window = wl_get_command_for_window,
|
||||
.icon_update_pixmap = wl_noop_icon_update_pixmap,
|
||||
.icon_paint = wl_noop_icon_paint,
|
||||
.icon_paint = wl_icon_paint,
|
||||
.appicon_show = wl_noop_appicon_show,
|
||||
.appicon_paint = wl_appicon_paint,
|
||||
.restore_desktop = wl_noop_restore_desktop,
|
||||
|
|
|
|||
|
|
@ -817,7 +817,6 @@ void wIconPaint(WIcon *icon)
|
|||
if (!icon || !icon->core || !icon->core->screen_ptr)
|
||||
return;
|
||||
|
||||
/* Wayland backend: icon windows have ID=None; skip X11 drawing. */
|
||||
if (!icon->core->window)
|
||||
return;
|
||||
|
||||
|
|
@ -830,6 +829,9 @@ void wIconPaint(WIcon *icon)
|
|||
if (icon->selected)
|
||||
wm_backend->icon_select_paint(icon, 0);
|
||||
|
||||
/* Allow the backend to composite the full icon (tile + image + title)
|
||||
* and commit it to its rendering surface. */
|
||||
wm_backend->icon_paint(icon);
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
|
|
|
|||
Loading…
Reference in New Issue