Phase C complete: balloon tooltips + window opacity
C1 (slide animations): already works - misc.c calls vtable window_move
C2 (menu scrolling): pure policy code, already works
C3 (balloon tooltips): pixman text render to wlr_scene_buffer overlay
C4 (cursor themes): wlr_xcursor_manager already in place
C5 (window opacity): read _NET_WM_WINDOW_OPACITY, apply to frame +
child buffers + client surface via wlr_scene_buffer_set_opacity
This commit is contained in:
parent
a0a27b56d5
commit
4c5f07f9ec
|
|
@ -98,6 +98,12 @@ Three major libraries:
|
|||
- Button scene_bufs raised above titlebar (commit a34d828)
|
||||
- Titlebar button rendering — proper colors, bevel, pressed state (commit 5a4d989)
|
||||
- Button bevel colors derived from titlebar background (commit f3cb593)
|
||||
- **Phase C complete** (2026-05-23):
|
||||
- C1: Slide animations — already works (misc.c calls vtable window_move per frame)
|
||||
- C2: Menu scrolling — pure policy code, already works
|
||||
- C3: Balloon tooltips — pixman text render → wlr_scene_buffer overlay in WL_LAYER_OVERLAY
|
||||
- C4: Cursor themes — wlr_xcursor_manager + wl_pointer_change_cursor already in place
|
||||
- C5: Window opacity — reads _NET_WM_WINDOW_OPACITY from XWayland, applies wlr_scene_buffer_set_opacity
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -136,11 +142,13 @@ Priority: HIGH — needed for daily-driver use.
|
|||
|
||||
| # | Task | Key Files | Complexity |
|
||||
|---|------|-----------|------------|
|
||||
| C1 | Slide animations (iconify/deiconify zoom) | vtable slide_window | Medium |
|
||||
| C2 | Menu scrolling (long menus) | menu.c scroll logic | Low |
|
||||
| C3 | Balloon tooltips (dock icon hover) | vtable balloon_show | Low |
|
||||
| C4 | Cursor themes (xcursor loading) | wlr_xcursor_manager | Low |
|
||||
| C5 | Window opacity (NETWM alpha) | vtable netwm_set_opacity | Low |
|
||||
| C1 | ~~Slide animations (iconify/deiconify zoom)~~ | misc.c → vtable window_move | ✅ Done (already works) |
|
||||
| C2 | ~~Menu scrolling (long menus)~~ | menu.c scroll logic | ✅ Done (pure policy) |
|
||||
| C3 | ~~Balloon tooltips (dock icon hover)~~ | vtable balloon_show | ✅ Done |
|
||||
| C4 | ~~Cursor themes (xcursor loading)~~ | wlr_xcursor_manager | ✅ Done (already works) |
|
||||
| C5 | ~~Window opacity (NETWM alpha)~~ | vtable netwm_update_opacity | ✅ Done |
|
||||
|
||||
**Phase C: COMPLETE**
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -254,11 +262,11 @@ NOW (daily-driver basics):
|
|||
NEXT (visual completeness):
|
||||
~~B1. App icon rendering (wrlib → pixman)~~ ✅
|
||||
~~F1. wrlib RContext for Wayland~~ ✅
|
||||
C1. Slide animations
|
||||
C2. Menu scrolling
|
||||
C3. Balloon tooltips
|
||||
C4. Cursor themes
|
||||
C5. Window opacity
|
||||
~~C1. Slide animations~~ ✅
|
||||
~~C2. Menu scrolling~~ ✅
|
||||
~~C3. Balloon tooltips~~ ✅
|
||||
~~C4. Cursor themes~~ ✅
|
||||
~~C5. Window opacity~~ ✅
|
||||
|
||||
THEN (full feature parity):
|
||||
D*. Test all WINGs widgets
|
||||
|
|
|
|||
|
|
@ -7680,7 +7680,56 @@ static void wl_noop_netwm_init(WScreen *scr) { (void)scr; }
|
|||
static void wl_noop_netwm_cleanup(WScreen *scr) { (void)scr; }
|
||||
static void wl_noop_netwm_update_desktop(WScreen *scr) { (void)scr; }
|
||||
static int wl_noop_netwm_get_current_desktop(WScreen *scr) { (void)scr; return -1; }
|
||||
static void wl_noop_netwm_update_opacity(WWindow *wwin) { (void)wwin; }
|
||||
static void wl_netwm_update_opacity(WWindow *wwin)
|
||||
{
|
||||
if (!wwin->frame)
|
||||
return;
|
||||
|
||||
struct wl_frame_buf *fb = frame_buf_find(wwin->frame->core->window);
|
||||
if (!fb || !fb->scene_buf)
|
||||
return;
|
||||
|
||||
float alpha = 1.0f;
|
||||
if (wl_state.x_display && wwin->client_win) {
|
||||
Atom type;
|
||||
int format;
|
||||
unsigned long items, rest;
|
||||
unsigned char *prop = NULL;
|
||||
Atom opacity_atom = XInternAtom(wl_state.x_display,
|
||||
"_NET_WM_WINDOW_OPACITY", False);
|
||||
if (XGetWindowProperty(wl_state.x_display, (Window)wwin->client_win,
|
||||
opacity_atom, 0L, 1L, False, AnyPropertyType,
|
||||
&type, &format, &items, &rest, &prop) == Success
|
||||
&& format == 32 && items == 1 && prop) {
|
||||
unsigned long val = *(unsigned long *)prop;
|
||||
alpha = (float)val / (float)0xFFFFFFFF;
|
||||
}
|
||||
if (prop)
|
||||
XFree(prop);
|
||||
}
|
||||
|
||||
wlr_scene_buffer_set_opacity(fb->scene_buf, alpha);
|
||||
|
||||
/* Apply to all child frame_bufs (titlebar buttons, resizebar) */
|
||||
struct wl_frame_buf *child;
|
||||
wl_list_for_each(child, &wl_state.frame_list, link) {
|
||||
if (child->parent_id == fb->id && child->scene_buf)
|
||||
wlr_scene_buffer_set_opacity(child->scene_buf, alpha);
|
||||
}
|
||||
|
||||
/* Apply to the client surface */
|
||||
struct wl_toplevel_view *v;
|
||||
wl_list_for_each(v, &wl_state.toplevel_list, link) {
|
||||
if (v->frame_id == fb->id && v->scene_node) {
|
||||
struct wlr_scene_node *node;
|
||||
wl_list_for_each(node, &v->scene_node->children, link) {
|
||||
if (node->type == WLR_SCENE_NODE_BUFFER)
|
||||
wlr_scene_buffer_set_opacity(
|
||||
wlr_scene_buffer_from_node(node), alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static void wl_noop_netwm_update_actions(WWindow *wwin, int del) { (void)wwin; (void)del; }
|
||||
static void wl_noop_netwm_check_client_hints(WWindow *wwin, int *layer, int *workspace)
|
||||
{ (void)wwin; (void)layer; (void)workspace; }
|
||||
|
|
@ -8187,6 +8236,35 @@ static void wl_drawer_icon_paint(WAppIcon *aicon)
|
|||
static void wl_balloon_init(WScreen *scr)
|
||||
{
|
||||
scr->balloon = wmalloc(sizeof(WBalloon));
|
||||
memset(scr->balloon, 0, sizeof(WBalloon));
|
||||
|
||||
/* Create a frame_buf in the overlay layer for balloon tooltips */
|
||||
if (wl_state.scene) {
|
||||
struct wl_frame_buf *fb = wmalloc(sizeof(*fb));
|
||||
memset(fb, 0, sizeof(*fb));
|
||||
|
||||
int w = 1, h = 1;
|
||||
fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h, NULL, 0);
|
||||
wlr_buffer_init(&fb->base, &frame_buf_impl, w, h);
|
||||
fb->id = wl_state.next_synthetic_id++;
|
||||
fb->width = w;
|
||||
fb->height = h;
|
||||
|
||||
struct wlr_scene_tree *parent = wl_state.level_trees[WL_LAYER_OVERLAY]
|
||||
? wl_state.level_trees[WL_LAYER_OVERLAY] : &wl_state.scene->tree;
|
||||
fb->tree = wlr_scene_tree_create(parent);
|
||||
if (fb->tree) {
|
||||
wlr_scene_node_set_position(&fb->tree->node, 0, 0);
|
||||
fb->scene_buf = wlr_scene_buffer_create(fb->tree, &fb->base);
|
||||
if (fb->scene_buf)
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, 0, 0);
|
||||
wlr_scene_node_set_enabled(&fb->tree->node, false);
|
||||
}
|
||||
|
||||
wl_list_insert(&wl_state.frame_list, &fb->link);
|
||||
W_RegisterBacking(fb->id, fb->image);
|
||||
scr->balloon->window = (Window)fb->id;
|
||||
}
|
||||
}
|
||||
static void wl_noop_do_kaboom(WScreen *scr, WNativeWindow win, int x, int y)
|
||||
{ (void)scr; (void)win; (void)x; (void)y; }
|
||||
|
|
@ -8462,7 +8540,77 @@ static void wl_noop_icon_select_paint(WIcon *icon, int step)
|
|||
{ (void)icon; (void)step; }
|
||||
static unsigned long wl_noop_window_get_event_mask(WNativeWindow window)
|
||||
{ (void)window; return 0; }
|
||||
static void wl_noop_balloon_show(WScreen *scr, void *bal) { (void)scr; (void)bal; }
|
||||
static void wl_balloon_show(WScreen *scr, void *bal_ptr)
|
||||
{
|
||||
WBalloon *bal = (WBalloon *)bal_ptr;
|
||||
if (!bal->text || !bal->text[0])
|
||||
return;
|
||||
|
||||
struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)bal->window);
|
||||
if (!fb)
|
||||
return;
|
||||
|
||||
WMFont *font = scr->info_text_font;
|
||||
int textlen = strlen(bal->text);
|
||||
int tw = WMWidthOfString(font, bal->text, textlen);
|
||||
int th = WMFontHeight(font);
|
||||
int pad = 4;
|
||||
int bw = tw + pad * 2;
|
||||
int bh = th + pad * 2;
|
||||
|
||||
/* Resize pixman buffer if needed */
|
||||
if (bw != fb->width || bh != fb->height) {
|
||||
W_UnregisterBacking(fb->id);
|
||||
pixman_image_unref(fb->image);
|
||||
fb->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, bw, bh, NULL, 0);
|
||||
fb->width = bw;
|
||||
fb->height = bh;
|
||||
fb->base.width = bw;
|
||||
fb->base.height = bh;
|
||||
W_RegisterBacking(fb->id, fb->image);
|
||||
}
|
||||
|
||||
/* Fill pale yellow background */
|
||||
uint32_t *data = pixman_image_get_data(fb->image);
|
||||
for (int i = 0; i < bw * bh; i++)
|
||||
data[i] = 0xFFFFFFE0;
|
||||
|
||||
/* 1px black border */
|
||||
for (int x = 0; x < bw; x++) {
|
||||
data[x] = 0xFF000000;
|
||||
data[(bh - 1) * bw + x] = 0xFF000000;
|
||||
}
|
||||
for (int y = 0; y < bh; y++) {
|
||||
data[y * bw] = 0xFF000000;
|
||||
data[y * bw + bw - 1] = 0xFF000000;
|
||||
}
|
||||
|
||||
/* Draw text */
|
||||
WMDrawString(scr->wmscreen, (WMDrawable)fb->id, scr->black, font,
|
||||
pad, pad, bal->text, textlen);
|
||||
|
||||
/* Position near objectWindow */
|
||||
int bx = 0, by = 0;
|
||||
struct wl_frame_buf *obj = frame_buf_find((WNativeWindow)bal->objectWindow);
|
||||
if (obj) {
|
||||
bx = obj->x;
|
||||
by = obj->y + bal->h + 2;
|
||||
}
|
||||
|
||||
/* Clamp to screen */
|
||||
if (bx + bw > scr->scr_width)
|
||||
bx = scr->scr_width - bw;
|
||||
if (by + bh > scr->scr_height)
|
||||
by = by - bal->h - bh - 2;
|
||||
|
||||
if (fb->scene_buf) {
|
||||
wlr_scene_node_set_position(&fb->scene_buf->node, bx, by);
|
||||
wlr_scene_buffer_set_buffer(fb->scene_buf, &fb->base);
|
||||
wlr_scene_node_set_enabled(&fb->tree->node, true);
|
||||
}
|
||||
|
||||
bal->mapped = 1;
|
||||
}
|
||||
static void wl_noop_window_fill_stipple(WNativeWindow win, int x, int y, int w, int h)
|
||||
{ (void)win; (void)x; (void)y; (void)w; (void)h; }
|
||||
static int wl_noop_event_check_enter_leave(void *dock) { (void)dock; return 1; }
|
||||
|
|
@ -8670,7 +8818,7 @@ const WMBackend wl_backend = {
|
|||
.netwm_cleanup = wl_noop_netwm_cleanup,
|
||||
.netwm_update_desktop = wl_noop_netwm_update_desktop,
|
||||
.netwm_get_current_desktop = wl_noop_netwm_get_current_desktop,
|
||||
.netwm_update_opacity = wl_noop_netwm_update_opacity,
|
||||
.netwm_update_opacity = wl_netwm_update_opacity,
|
||||
.netwm_update_actions = wl_noop_netwm_update_actions,
|
||||
.netwm_check_client_hints = wl_noop_netwm_check_client_hints,
|
||||
.netwm_check_initial_state = wl_noop_netwm_check_initial_state,
|
||||
|
|
@ -8743,7 +8891,7 @@ const WMBackend wl_backend = {
|
|||
.icon_paint_title_bg = wl_noop_icon_paint_title_bg,
|
||||
.icon_select_paint = wl_noop_icon_select_paint,
|
||||
.window_get_event_mask = wl_noop_window_get_event_mask,
|
||||
.balloon_show = wl_noop_balloon_show,
|
||||
.balloon_show = wl_balloon_show,
|
||||
.window_fill_stipple = wl_noop_window_fill_stipple,
|
||||
.event_check_enter_leave = wl_noop_event_check_enter_leave,
|
||||
.event_allow_async = wl_noop_event_allow_async,
|
||||
|
|
|
|||
Loading…
Reference in New Issue