From b663ab836062395cef1306824e8a4b9b2455d3c2 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Wed, 13 May 2026 14:27:11 +0200 Subject: [PATCH] Implement Wayland text rendering + frame titlebar/resizebar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Text rendering uses the existing WINGs PangoFT2 backend — WMDrawString renders directly into the frame buffer's pixman_image_t. - Menu entries: real text via PangoFT2 (entry text + shortcut labels) - Dock/clip icon titles: rendered with icon_title_font - Frame titlebar: solid background + centered title text - Frame resizebar: beveled grey bar All text is anti-aliased (PangoFT2 alpha compositing onto pixman). --- src/backend/wayland/wl_backend.c | 127 ++++++++++++++++++++++++++----- 1 file changed, 108 insertions(+), 19 deletions(-) diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index 70d130a..eab271b 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -6199,8 +6199,17 @@ static void wl_appicon_paint(WAppIcon *aicon) /* Draw title text */ if (icon->show_title && icon->icon_name && scr->icon_title_font) { - /* Simple: just render text at bottom of icon */ - /* TODO: proper text rendering with pango/pixman */ + WMDrawable d = (WMDrawable)(uintptr_t)fb->image; + int fh = WMFontHeight(scr->icon_title_font); + int tw = WMWidthOfString(scr->icon_title_font, icon->icon_name, + strlen(icon->icon_name)); + int tx = (size - tw) / 2; + if (tx < 2) tx = 2; + int ty = fh; /* baseline near top */ + WMColor *color = scr->icon_title_color; + if (color) + WMDrawString(scr->wmscreen, d, color, scr->icon_title_font, + tx, ty, icon->icon_name, strlen(icon->icon_name)); } RReleaseImage(tile); @@ -6375,29 +6384,34 @@ static void wl_menu_paint_entry(WMenu *menu, int index, int selected) /* Background color */ uint32_t bg; if (selected) { - bg = 0xFF4A6984; /* selection blue */ + bg = 0xFF4A6984; } else { - bg = 0xFFAEAAAE; /* menu grey */ + bg = 0xFFAEAAAE; } - /* Fill entry background */ for (int row = y; row < y + h && row < fb->height; row++) for (int col = 0; col < w; col++) dst[row * stride + col] = bg; - /* Text color */ - uint32_t fg = selected ? 0xFFFFFFFF : (entry->flags.enabled ? 0xFF000000 : 0xFF666666); + /* Draw text using WINGs font backend (renders via PangoFT2 → pixman) */ + WMColor *color = selected ? scr->select_text_color : + (entry->flags.enabled ? scr->mtext_color : scr->dtext_color); + WMFont *font = scr->menu_entry_font; + WMDrawable d = (WMDrawable)(uintptr_t)fb->image; - /* Simple 1px text placeholder: draw a line where text would be */ - int text_y = y + h / 2; - int text_len = entry->text ? strlen(entry->text) * 7 : 0; - if (text_len > w - 16) text_len = w - 16; - if (text_y < fb->height) { - for (int col = 8; col < 8 + text_len && col < w; col++) - dst[text_y * stride + col] = fg; + if (entry->text && font && color) { + int text_y = y + 3 + WMFontHeight(font) - 1; + WMDrawString(scr->wmscreen, d, color, font, + 8, text_y, entry->text, strlen(entry->text)); + } + + if (entry->rtext && entry->cascade < 0 && font && color) { + int tw = WMWidthOfString(font, entry->rtext, strlen(entry->rtext)); + int text_y = y + 3 + WMFontHeight(font) - 1; + WMDrawString(scr->wmscreen, d, color, font, + w - 8 - tw, text_y, entry->rtext, strlen(entry->rtext)); } - (void)scr; wlr_scene_buffer_set_buffer(fb->scene_buf, &fb->base); } @@ -6460,8 +6474,83 @@ static unsigned long wl_keysym_from_name(const char *name) return (unsigned long)xkb_keysym_from_name(name, XKB_KEYSYM_NO_FLAGS); } -static void wl_noop_frame_paint_resizebar(WFrameWindow *fwin) { (void)fwin; } -static void wl_noop_frame_paint_titlebar(WFrameWindow *fwin, int state) { (void)fwin; (void)state; } +static void wl_frame_paint_resizebar(WFrameWindow *fwin) +{ + if (!fwin->resizebar || !fwin->resizebar->window) + return; + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)fwin->resizebar->window); + if (!fb || !fb->image) + return; + + uint32_t *dst = (uint32_t *)pixman_image_get_data(fb->image); + int stride = pixman_image_get_stride(fb->image) / 4; + uint32_t bg = 0xFFAEAAAE; + uint32_t light = 0xFFDDDDDD; + uint32_t dark = 0xFF555555; + + /* Fill background */ + for (int row = 0; row < fb->height; row++) + for (int col = 0; col < fb->width; col++) + dst[row * stride + col] = bg; + /* Top highlight */ + for (int col = 0; col < fb->width; col++) + dst[col] = light; + /* Bottom shadow */ + if (fb->height > 1) + for (int col = 0; col < fb->width; col++) + dst[(fb->height - 1) * stride + col] = dark; + + wlr_scene_buffer_set_buffer(fb->scene_buf, &fb->base); +} + +static void wl_frame_paint_titlebar(WFrameWindow *fwin, int state) +{ + if (!fwin->titlebar || !fwin->titlebar->window) + return; + struct wl_frame_buf *fb = frame_buf_find((WNativeWindow)fwin->titlebar->window); + if (!fb || !fb->image) + return; + + WScreen *scr = fwin->screen_ptr; + uint32_t *dst = (uint32_t *)pixman_image_get_data(fb->image); + int stride = pixman_image_get_stride(fb->image) / 4; + + /* Background: use texture color or default */ + uint32_t bg; + if (fwin->title_texture[state] && + fwin->title_texture[state]->any.type == WTEX_SOLID) { + unsigned long px = fwin->title_texture[state]->solid.normal.pixel; + bg = 0xFF000000 | (px & 0xFFFFFF); + } else { + bg = (state == 0) ? 0xFF4A6984 : 0xFF808080; /* focused blue / unfocused grey */ + } + + for (int row = 0; row < fb->height; row++) + for (int col = 0; col < fb->width; col++) + dst[row * stride + col] = bg; + + /* Draw title text */ + if (fwin->title && fwin->font && fwin->title_color[state]) { + char *title = ShrinkString(*fwin->font, fwin->title, fb->width - 12); + int titlelen = strlen(title); + int tw = WMWidthOfString(*fwin->font, title, titlelen); + int x; + switch (fwin->flags.justification) { + case WTJ_LEFT: x = 6; break; + case WTJ_RIGHT: x = fb->width - tw - 6; break; + default: x = (fb->width - tw) / 2; break; + } + int fh = WMFontHeight(*fwin->font); + int y = (fb->height - fh) / 2 + fh; + + WMDrawable d = (WMDrawable)(uintptr_t)fb->image; + WMDrawString(scr->wmscreen, d, fwin->title_color[state], + *fwin->font, x, y, title, titlelen); + wfree(title); + } + + wlr_scene_buffer_set_buffer(fb->scene_buf, &fb->base); +} static WNativeWindow wl_noop_prop_get_client_leader(WNativeWindow window) { (void)window; return WNATIVE_NONE; } static int wl_noop_prop_get_window_state(WNativeWindow window) { (void)window; return -1; } @@ -6757,8 +6846,8 @@ const WMBackend wl_backend = { .pixmap_create_from_xbm = wl_noop_pixmap_create_from_xbm, .pixmap_create = wl_noop_pixmap_create, .keysym_from_name = wl_keysym_from_name, - .frame_paint_resizebar = wl_noop_frame_paint_resizebar, - .frame_paint_titlebar = wl_noop_frame_paint_titlebar, + .frame_paint_resizebar = wl_frame_paint_resizebar, + .frame_paint_titlebar = wl_frame_paint_titlebar, .prop_get_client_leader = wl_noop_prop_get_client_leader, .prop_get_window_state = wl_noop_prop_get_window_state, .setup_modifier_lock_masks = wl_noop_setup_modifier_lock_masks,