diff --git a/src/backend/backend.h b/src/backend/backend.h index 8b7e87b..7b414ad 100644 --- a/src/backend/backend.h +++ b/src/backend/backend.h @@ -468,6 +468,13 @@ typedef struct WMBackend { */ void *(*window_get_icon_image)(WNativeWindow win); + /* + * Create an RImage from a drawable (window or pixmap ID). + * Used by MakeGhostIcon to read pixel data from an icon's frame buffer. + * Returns a new RImage* or NULL. Caller must RReleaseImage. + */ + void *(*create_rimage_from_drawable)(void *rcontext, WMDrawable drawable); + /* * Read the _WINDOWMAKER_MENU text property from win. * On success stores an XFreeStringList-allocated string array in *list_out, diff --git a/src/backend/wayland/wl_dock.c b/src/backend/wayland/wl_dock.c index 688257f..3bd595c 100644 --- a/src/backend/wayland/wl_dock.c +++ b/src/backend/wayland/wl_dock.c @@ -102,6 +102,10 @@ wm_dock_render(struct wm_shm_surface *s, int icon_sz, int n_slots) void wm_dock_create(WScreen *scr) { + /* Disabled: the real wmaker dock framework handles icons via frame_bufs. + * This shm_surface panel was a placeholder. */ + (void)scr; + return; if (!wl_state.scene || !wl_state.output) return; @@ -273,6 +277,8 @@ wm_dock_refresh(void) void wm_dock_app_add(struct wl_toplevel_view *v) { + (void)v; + return; /* Disabled: real dock handles icons via frame_bufs */ if (!wl_state.dock_surface) return; if (wl_state.dock_app_count >= WM_DOCK_MAX_APPS) return; @@ -298,6 +304,8 @@ wm_dock_app_add(struct wl_toplevel_view *v) void wm_dock_app_remove(WNativeWindow id) { + (void)id; + return; /* Disabled: real dock handles icons via frame_bufs */ if (!wl_state.dock_surface) return; for (int i = 0; i < wl_state.dock_app_count; i++) { diff --git a/src/backend/wayland/wl_event.c b/src/backend/wayland/wl_event.c index 91d1d79..ffa53ac 100644 --- a/src/backend/wayland/wl_event.c +++ b/src/backend/wayland/wl_event.c @@ -1,6 +1,8 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include +#include #include "../../screen.h" #include "wl_types.h" @@ -8,6 +10,9 @@ void wl_event_flush(void) { wl_xdisplay_flush(); + /* Commit the scene so intermediate animation frames are visible */ + if (wl_state.scene_output && wl_state.allocator) + wlr_scene_output_commit(wl_state.scene_output, NULL); } void wl_event_flush_expose(void) {} void wl_event_sync(int discard) diff --git a/src/backend/wayland/wl_icon.c b/src/backend/wayland/wl_icon.c index 8ec7e3e..905d570 100644 --- a/src/backend/wayland/wl_icon.c +++ b/src/backend/wayland/wl_icon.c @@ -280,3 +280,80 @@ void wl_drawer_icon_paint(WAppIcon *aicon) wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); } +/* Create an RImage by reading pixels from a frame_buf's pixman image */ +void * +wl_create_rimage_from_drawable(void *rcontext, WMDrawable drawable) +{ + (void)rcontext; + if (!drawable) + return NULL; + + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)drawable); + if (!fb || !fb->image) + return NULL; + + int w = pixman_image_get_width(fb->image); + int h = pixman_image_get_height(fb->image); + if (w <= 0 || h <= 0) + return NULL; + + RImage *img = RCreateImage(w, h, 1); + if (!img) + return NULL; + + uint32_t *src = (uint32_t *)pixman_image_get_data(fb->image); + int stride = pixman_image_get_stride(fb->image) / 4; + unsigned char *dst = img->data; + + for (int row = 0; row < h; row++) { + for (int col = 0; col < w; col++) { + uint32_t px = src[row * stride + col]; + *dst++ = (px >> 16) & 0xFF; /* R */ + *dst++ = (px >> 8) & 0xFF; /* G */ + *dst++ = px & 0xFF; /* B */ + *dst++ = (px >> 24) & 0xFF; /* A */ + } + } + + return img; +} + +/* Re-render icon pixmap; applies shadowed (dimmed) overlay for attracted icons */ +void +wl_icon_update_pixmap(WIcon *icon) +{ + if (!icon || !icon->core) + return; + + /* Find the owning appicon and repaint it */ + WAppIcon *aicon = (WAppIcon *)icon->core->descriptor.parent; + if (aicon && icon->core->descriptor.parent_type == WCLASS_APPICON) + wl_appicon_paint(aicon); + else if (aicon && icon->core->descriptor.parent_type == WCLASS_DOCK_ICON) + wl_appicon_paint(aicon); + else + wl_icon_paint(icon); + + /* Apply shadowed overlay (semi-transparent dark wash) for attracted icons */ + if (icon->shadowed) { + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)icon->core->window); + if (fb && fb->image) { + int w = pixman_image_get_width(fb->image); + int h = pixman_image_get_height(fb->image); + uint32_t *px = (uint32_t *)pixman_image_get_data(fb->image); + int stride = pixman_image_get_stride(fb->image) / 4; + for (int row = 0; row < h; row++) { + for (int col = 0; col < w; col++) { + uint32_t p = px[row * stride + col]; + uint32_t a = (p >> 24) & 0xFF; + uint32_t r = ((p >> 16) & 0xFF) / 2; + uint32_t g = ((p >> 8) & 0xFF) / 2; + uint32_t b = (p & 0xFF) / 2; + px[row * stride + col] = (a << 24) | (r << 16) | (g << 8) | b; + } + } + wlr_scene_buffer_set_buffer_with_damage(fb->scene_buf, &fb->base, NULL); + } + } +} + diff --git a/src/backend/wayland/wl_stubs.c b/src/backend/wayland/wl_stubs.c index 6086e04..e192e68 100644 --- a/src/backend/wayland/wl_stubs.c +++ b/src/backend/wayland/wl_stubs.c @@ -44,6 +44,8 @@ void wl_netwm_update_opacity(WWindow *wwin); void wl_icon_paint(WIcon *icon); void wl_appicon_show(WApplication *wapp); void wl_appicon_paint(WAppIcon *aicon); +void *wl_create_rimage_from_drawable(void *rcontext, WMDrawable drawable); +void wl_icon_update_pixmap(WIcon *icon); void wl_client_set_state(WWindow *wwin, int state, WNativeWindow icon_win); void wl_client_set_maximized(WNativeWindow win, int maximized); void wl_client_send_protocol(WWindow *wwin, unsigned long protocol, unsigned long timestamp); @@ -325,6 +327,7 @@ const WMBackend wl_backend = { .window_get_icon_name = wl_window_get_icon_name, .screen_get_head_usable_area = wl_noop_screen_get_head_usable_area, .window_get_icon_image = wl_window_get_icon_image, + .create_rimage_from_drawable = wl_create_rimage_from_drawable, .prop_get_wmaker_menu = wl_prop_get_wmaker_menu, .prop_get_transient_for = wl_prop_get_transient_for, .prop_has_wmaker_menu = wl_prop_has_wmaker_menu, @@ -449,7 +452,7 @@ const WMBackend wl_backend = { .get_text_selection = wl_noop_get_text_selection, .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_update_pixmap = wl_icon_update_pixmap, .icon_paint = wl_icon_paint, .appicon_show = wl_appicon_show, .appicon_paint = wl_appicon_paint, diff --git a/src/backend/wayland/wl_xdg.c b/src/backend/wayland/wl_xdg.c index b835bac..f23236f 100644 --- a/src/backend/wayland/wl_xdg.c +++ b/src/backend/wayland/wl_xdg.c @@ -18,6 +18,7 @@ #include "../../actions.h" #include "../../xinerama.h" #include "../../framewin.h" +#include "../../application.h" #include /* Forward declarations for functions defined in other wl_*.c files */ @@ -393,7 +394,16 @@ handle_toplevel_unmap(struct wl_listener *listener, void *data) WObjDescriptor *desc = wm_backend->context_find(view->id, WM_CTX_CLIENT_WIN); if (desc && desc->parent_type == WCLASS_WINDOW) { WWindow *wwin = (WWindow *)desc->parent; + WNativeWindow main_win = wwin->main_window; wUnmanageWindow(wwin, False, True); + /* Destroy the WApplication (and its appicon) if this was the + * main window. Mirrors handleDestroyNotify in event.c. */ + WApplication *app = wApplicationOf(main_win); + if (app) { + if (main_win == app->main_window) + app->refcount = 0; + wApplicationDestroy(app); + } } } } diff --git a/src/backend/x11/x11_backend.c b/src/backend/x11/x11_backend.c index aef2aed..1d66bdc 100644 --- a/src/backend/x11/x11_backend.c +++ b/src/backend/x11/x11_backend.c @@ -1356,6 +1356,12 @@ x11_window_get_icon_image(WNativeWindow win) return get_window_image_from_x11(dpy, WNW_TO_XID(win)); } +static void * +x11_create_rimage_from_drawable(void *rcontext, WMDrawable drawable) +{ + return RCreateImageFromDrawable((RContext *)rcontext, (Drawable)drawable, None); +} + static int x11_prop_get_wmaker_menu(WNativeWindow win, char ***list_out, int *count_out) { @@ -4795,6 +4801,7 @@ const WMBackend x11_backend = { .window_get_icon_name = x11_window_get_icon_name, .screen_get_head_usable_area = x11_screen_get_head_usable_area, .window_get_icon_image = x11_window_get_icon_image, + .create_rimage_from_drawable = x11_create_rimage_from_drawable, .prop_get_wmaker_menu = x11_prop_get_wmaker_menu, .monitors_query = x11_monitors_query, .monitors_select_events = x11_monitors_select_events, diff --git a/src/superfluous.c b/src/superfluous.c index bebec68..b4c5a0c 100644 --- a/src/superfluous.c +++ b/src/superfluous.c @@ -61,9 +61,12 @@ WMNativePixmap MakeGhostIcon(WScreen * scr, WMDrawable drawable) if (!drawable) return WM_NATIVE_NONE; - back = RCreateImageFromDrawable(scr->rcontext, drawable, WM_NATIVE_NONE); - if (!back) + back = (RImage *)wm_backend->create_rimage_from_drawable(scr->rcontext, drawable); + if (!back) { + wwarning("MakeGhostIcon: create_rimage_from_drawable failed for drawable=0x%lx", (unsigned long)drawable); return WM_NATIVE_NONE; + } + wwarning("MakeGhostIcon: got RImage %dx%d from drawable=0x%lx", back->width, back->height, (unsigned long)drawable); color.red = 0xff; color.green = 0xff; @@ -71,7 +74,12 @@ WMNativePixmap MakeGhostIcon(WScreen * scr, WMDrawable drawable) color.alpha = 200; RClearImage(back, &color); - RConvertImage(scr->rcontext, back, &pixmap); + if (!RConvertImage(scr->rcontext, back, &pixmap)) { + wwarning("MakeGhostIcon: RConvertImage failed"); + RReleaseImage(back); + return WM_NATIVE_NONE; + } + wwarning("MakeGhostIcon: converted to pixmap=0x%lx", (unsigned long)pixmap); RReleaseImage(back); diff --git a/src/window.c b/src/window.c index 1440f30..633870f 100644 --- a/src/window.c +++ b/src/window.c @@ -749,6 +749,15 @@ WWindow *wManageWindow(WScreen *scr, WNativeWindow window) if (WFLAGP(wwin, emulate_appicon)) wwin->main_window = wwin->client_win; + /* Wayland native clients have no client_leader or window_group. + * Treat each non-transient window as its own app leader so that + * wApplicationCreate runs and an appicon is produced. */ + if (wwin->main_window == WM_NATIVE_NONE + && wwin->transient_for == WM_NATIVE_NONE + && wwin->client_leader == WM_NATIVE_NONE + && wwin->group_id == WM_NATIVE_NONE) + wwin->main_window = wwin->client_win; + fixLeaderProperties(wwin); wwin->orig_main_window = wwin->main_window;