diff --git a/meson.build b/meson.build index c4e27fd5..47b78428 100644 --- a/meson.build +++ b/meson.build @@ -37,6 +37,9 @@ pcre2_dep = dependency('libpcre2-8') libscenefx_dep = dependency('scenefx-0.4',version: '>=0.4.1') pixman_dep = dependency('pixman-1') cjson_dep = dependency('libcjson') +cairo_dep = dependency('cairo') +pango_dep = dependency('pango') +pangocairo_dep = dependency('pangocairo') # version info git = find_program('git', required : false) @@ -94,6 +97,7 @@ endif executable('mango', 'src/mango.c', 'src/common/util.c', + 'src/draw/text-node.c', 'src/ext-protocol/wlr_ext_workspace_v1.c', wayland_sources, dependencies : [ @@ -109,6 +113,9 @@ executable('mango', pcre2_dep, pixman_dep, cjson_dep, + cairo_dep, + pango_dep, + pangocairo_dep, ], install : true, c_args : c_args, diff --git a/src/draw/text-node.c b/src/draw/text-node.c new file mode 100644 index 00000000..638ab1fe --- /dev/null +++ b/src/draw/text-node.c @@ -0,0 +1,140 @@ +#include "text-node.h" +#include +#include +#include +#include +#include +#include + +// 自定义 wlr_buffer,用于将 Cairo Surface 包装并注入 wlr_scene +struct mango_text_buffer { + struct wlr_buffer base; + cairo_surface_t *surface; +}; + +static void text_buffer_destroy(struct wlr_buffer *wlr_buffer) { + struct mango_text_buffer *buf = wl_container_of(wlr_buffer, buf, base); + cairo_surface_destroy(buf->surface); + free(buf); +} + +static bool text_buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, + uint32_t flags, + void **data, + uint32_t *format, + size_t *stride) { + (void)flags; // 该参数当前无需使用 + struct mango_text_buffer *buf = wl_container_of(wlr_buffer, buf, base); + *data = cairo_image_surface_get_data(buf->surface); + *format = DRM_FORMAT_ARGB8888; + *stride = cairo_image_surface_get_stride(buf->surface); + return true; +} + +static void text_buffer_end_data_ptr_access(struct wlr_buffer *wlr_buffer) { + // Cairo 表面数据在 flush 后稳定,此处无需额外操作 +} + +static const struct wlr_buffer_impl text_buffer_impl = { + .destroy = text_buffer_destroy, + .begin_data_ptr_access = text_buffer_begin_data_ptr_access, + .end_data_ptr_access = text_buffer_end_data_ptr_access, +}; + +struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent) { + struct mango_text_node *node = calloc(1, sizeof(*node)); + if (!node) return NULL; + + node->scene_buffer = wlr_scene_buffer_create(parent, NULL); + if (!node->scene_buffer) { + free(node); + return NULL; + } + return node; +} + +void mango_text_node_update(struct mango_text_node *node, const char *text, + const char *font_desc, float color[4], float scale) { + if (!node || !text || !font_desc) return; + if (scale <= 0.0f) scale = 1.0f; + + // 测量文字像素尺寸 + cairo_surface_t *dummy_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); + cairo_t *dummy_cr = cairo_create(dummy_surface); + PangoContext *dummy_ctx = pango_cairo_create_context(dummy_cr); + pango_cairo_context_set_resolution(dummy_ctx, 96.0 * scale); + PangoLayout *dummy_layout = pango_layout_new(dummy_ctx); + PangoFontDescription *desc = pango_font_description_from_string(font_desc); + pango_layout_set_font_description(dummy_layout, desc); + pango_layout_set_text(dummy_layout, text, -1); + + int pixel_width, pixel_height; + pango_layout_get_pixel_size(dummy_layout, &pixel_width, &pixel_height); + + g_object_unref(dummy_layout); + pango_font_description_free(desc); + g_object_unref(dummy_ctx); + cairo_destroy(dummy_cr); + cairo_surface_destroy(dummy_surface); + + if (pixel_width <= 0 || pixel_height <= 0) { + wlr_scene_buffer_set_buffer(node->scene_buffer, NULL); + node->logical_width = 0; + node->logical_height = 0; + return; + } + + // 创建真实大小的 Surface 并绘制文字 + cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, pixel_width, pixel_height); + cairo_t *cr = cairo_create(surface); + + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + + PangoContext *ctx = pango_cairo_create_context(cr); + pango_cairo_context_set_resolution(ctx, 96.0 * scale); + PangoLayout *layout = pango_layout_new(ctx); + desc = pango_font_description_from_string(font_desc); + pango_layout_set_font_description(layout, desc); + pango_layout_set_text(layout, text, -1); + + cairo_set_source_rgba(cr, color[0], color[1], color[2], color[3]); + pango_cairo_show_layout(cr, layout); + cairo_surface_flush(surface); + + // 包装成 wlr_buffer + struct mango_text_buffer *buf = calloc(1, sizeof(*buf)); + if (!buf) { + cairo_surface_destroy(surface); + cairo_destroy(cr); + g_object_unref(layout); + pango_font_description_free(desc); + g_object_unref(ctx); + return; + } + wlr_buffer_init(&buf->base, &text_buffer_impl, pixel_width, pixel_height); + buf->surface = surface; + + // 提交给场景图 + wlr_scene_buffer_set_buffer(node->scene_buffer, &buf->base); + wlr_buffer_drop(&buf->base); // 函数内引用计数 -1,交给场景图管理 + + // 设置逻辑大小,实现 HiDPI 缩放 + node->logical_width = (int)(pixel_width / scale); + node->logical_height = (int)(pixel_height / scale); + wlr_scene_buffer_set_dest_size(node->scene_buffer, node->logical_width, node->logical_height); + + // 清理绘制资源 + g_object_unref(layout); + pango_font_description_free(desc); + g_object_unref(ctx); + cairo_destroy(cr); +} + +void mango_text_node_destroy(struct mango_text_node *node) { + if (!node) return; + wlr_scene_node_destroy(&node->scene_buffer->node); + free(node); +} \ No newline at end of file diff --git a/src/draw/text-node.h b/src/draw/text-node.h new file mode 100644 index 00000000..36fb8026 --- /dev/null +++ b/src/draw/text-node.h @@ -0,0 +1,34 @@ +#ifndef MANGO_TEXT_H +#define MANGO_TEXT_H + +#include + +struct mango_text_node { + struct wlr_scene_buffer *scene_buffer; + int logical_width; + int logical_height; +}; + +/** + * 创建一个独立的文字场景节点 + * @param parent 父级场景树节点(例如窗口装饰树、OSD层树或状态栏树) + */ +struct mango_text_node *mango_text_node_create(struct wlr_scene_tree *parent); + +/** + * 更新节点中的文字内容、字体、颜色和缩放 + * @param node 文字节点指针 + * @param text 要显示的文本内容 + * @param font_desc Pango 字体描述字符串 (例如 "Sans 12" 或 "JetBrains Mono Bold 14") + * @param color RGBA 颜色数组,范围 0.0 ~ 1.0 + * @param scale 当前输出设备的缩放比例 (HiDPI 支持) + */ +void mango_text_node_update(struct mango_text_node *node, const char *text, + const char *font_desc, float color[4], float scale); + +/** + * 销毁文字节点并释放相关场景图资源 + */ +void mango_text_node_destroy(struct mango_text_node *node); + +#endif /* MANGO_TEXT_H */ \ No newline at end of file diff --git a/src/mango.c b/src/mango.c index 3f8362f4..85cf5840 100644 --- a/src/mango.c +++ b/src/mango.c @@ -96,6 +96,7 @@ #include #endif #include "common/util.h" +#include "draw/text-node.h" /* macros */ #define MAX(A, B) ((A) > (B) ? (A) : (B)) @@ -327,6 +328,7 @@ struct Client { struct wlr_scene_shadow *shadow; struct wlr_scene_tree *scene_surface; struct wlr_scene_tree *overview_scene_surface; + struct mango_text_node *text_node; struct wl_list link; struct wl_list flink; struct wl_list fadeout_link; @@ -4513,6 +4515,11 @@ mapnotify(struct wl_listener *listener, void *data) { #endif // extra node + c->text_node = mango_text_node_create(c->scene); + mango_text_node_update(c->text_node, "hello world", "Sans 50", config.focuscolor, 1.0f); + wlr_scene_node_lower_to_bottom(&c->text_node->scene_buffer->node); + wlr_scene_node_set_enabled(&c->text_node->scene_buffer->node, false); + for (i = 0; i < 2; i++) { c->splitindicator[i] = wlr_scene_rect_create( c->scene, 0, 0,